1#![doc = include_str!("../README.md")]
2#![no_std]
3#![allow(non_camel_case_types)]
4mod bcd;
5mod data;
6mod display;
7mod error;
8mod isoiec646;
9mod parse;
10mod utils;
11use crate::display::{fmt_naddr, fmt_naddr_type};
12use crate::parse::parse_nsap;
13use crate::utils::{u8_to_decimal_bytes, u16_to_decimal_bytes};
14pub use bcd::*;
15use core::convert::TryFrom;
16use core::fmt::Display;
17use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4};
18use core::str::FromStr;
19pub use data::*;
20pub use error::*;
21pub use isoiec646::*;
22
23#[cfg(feature = "alloc")]
24extern crate alloc;
25#[cfg(feature = "alloc")]
26use alloc::string::String;
27#[cfg(feature = "alloc")]
28use alloc::vec::Vec;
29
30pub type AFI = u8;
32
33pub type Rfc1277NetworkId = u8;
35
36pub type Rfc1277TransportSet = u16;
38
39pub const DEFAULT_ITOT_TRANSPORT_SET: Rfc1277TransportSet = 1;
41
42pub type Rfc1277SocketInfo = (Rfc1277NetworkId, SocketAddrV4, Rfc1277TransportSet);
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum DSPSyntax {
48 Decimal,
51 Binary,
53 IsoIec646Chars,
55 NationalChars,
57}
58
59#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
61pub enum X213NetworkAddressType {
62 X121,
74 ISO_DCC,
86 F69,
101 E163,
108 E164,
123 ISO_6523_ICD,
125 IANA_ICP,
133 ITU_T_IND,
135 LOCAL,
137 URL,
141}
142
143impl Display for X213NetworkAddressType {
144 #[inline]
147 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148 fmt_naddr_type(self, f)
149 }
150}
151
152impl TryFrom<AFI> for X213NetworkAddressType {
153 type Error = ();
154
155 #[inline]
156 fn try_from(value: AFI) -> Result<Self, Self::Error> {
157 afi_to_network_type(value).ok_or(())
158 }
159}
160
161#[derive(Debug)]
193pub enum X213NetworkAddress<'a> {
194 #[cfg(feature = "alloc")]
196 Heap(Vec<u8>),
197 Inline((u8, [u8; 22])),
202 Borrowed(&'a [u8]),
204}
205
206impl<'a> X213NetworkAddress<'a> {
207 #[inline]
209 pub fn get_octets(&'a self) -> &'a [u8] {
210 match &self {
211 #[cfg(feature = "alloc")]
212 X213NetworkAddress::Heap(o) => o.as_ref(),
213 X213NetworkAddress::Inline((sz, buf)) => &buf[0..(*sz).clamp(0u8, 20u8) as usize],
214 X213NetworkAddress::Borrowed(o) => *o,
215 }
216 }
217
218 #[inline]
220 pub fn afi(&self) -> u8 {
221 if self.get_octets().len() > 0 {
222 self.get_octets()[0]
223 } else {
224 panic!("Zero-length IDP in an X.213 network address")
225 }
226 }
227
228 #[cfg(feature = "alloc")]
230 pub fn from_vec_unchecked(octets: Vec<u8>) -> X213NetworkAddress<'static> {
231 X213NetworkAddress::Heap(octets)
232 }
233
234 #[cfg(feature = "alloc")]
236 pub fn from_vec(octets: Vec<u8>) -> Result<X213NetworkAddress<'static>, NAddressParseError> {
237 validate_raw_nsap(octets.as_ref())?;
238 Ok(X213NetworkAddress::Heap(octets))
239 }
240
241 pub fn from_slice_unchecked(octets: &'a [u8]) -> X213NetworkAddress<'a> {
243 X213NetworkAddress::Borrowed(octets)
244 }
245
246 pub fn from_slice(octets: &'a [u8]) -> Result<X213NetworkAddress<'a>, NAddressParseError> {
248 validate_raw_nsap(octets.as_ref())?;
249 Ok(X213NetworkAddress::Borrowed(octets))
250 }
251
252 #[inline]
254 pub fn get_network_type_info(&self) -> Option<X213NetworkAddressInfo> {
255 get_nsap_address_schema(self.afi())
256 }
257
258 #[inline]
260 pub fn get_network_type(&self) -> Option<X213NetworkAddressType> {
261 afi_to_network_type(self.afi())
262 }
263
264 pub fn idi_digits(&'a self) -> Option<BCDDigitsIter<'a>> {
270 let addr_type_info = get_nsap_address_schema(self.afi())?;
271 let leading_0_sig = addr_type_info.leading_zeroes_in_idi;
272 let is_dsp_decimal = matches!(addr_type_info.dsp_syntax, DSPSyntax::Decimal);
273 let idi_len = addr_type_info.max_idi_len_digits as usize;
274 let idi_len_in_bytes = (idi_len >> 1) + (idi_len % 2);
275 let odd_len_idi: bool = (idi_len % 2) > 0;
276 let octets = self.get_octets();
277 let idi = &octets[1..1 + idi_len_in_bytes];
278 Some(BCDDigitsIter::new(
279 idi,
280 leading_0_sig,
281 is_dsp_decimal && odd_len_idi,
282 false,
283 true,
284 ))
285 }
286
287 pub fn dsp_digits(&'a self) -> Option<BCDDigitsIter<'a>> {
293 let addr_type_info = get_nsap_address_schema(self.afi())?;
294 let is_dsp_decimal = matches!(addr_type_info.dsp_syntax, DSPSyntax::Decimal);
295 if !is_dsp_decimal {
296 return None;
297 }
298 let idi_len = addr_type_info.max_idi_len_digits as usize;
299 let idi_len_in_bytes = (idi_len >> 1) + (idi_len % 2);
300 let odd_len_idi: bool = (idi_len % 2) > 0;
301 let octets = self.get_octets();
302 let (dsp, start_on_lsn) = if is_dsp_decimal && odd_len_idi {
304 (&octets[idi_len_in_bytes..], true)
305 } else {
306 (&octets[1 + idi_len_in_bytes..], false)
307 };
308 Some(BCDDigitsIter::new(
309 dsp,
310 false, false, start_on_lsn,
313 false,
314 ))
315 }
316
317 pub fn get_url(&'a self) -> Option<&'a str> {
321 let octets = self.get_octets();
322 if octets.len() <= 5 || octets[0] != AFI_URL {
324 return None;
325 }
326 str::from_utf8(&octets[3..]).ok()
327 }
328
329 pub fn get_ip(&self) -> Option<IpAddr> {
336 let octets = self.get_octets();
337 if octets.len() < 7 || octets[0] != AFI_IANA_ICP_BIN {
338 return None;
339 }
340 match (octets[1], octets[2]) {
342 (0, 0) => {
343 if octets.len() < 19 {
345 return None;
346 }
347 let ip = Ipv6Addr::from([
348 octets[3], octets[4], octets[5], octets[6], octets[7], octets[8], octets[9],
349 octets[10], octets[11], octets[12], octets[13], octets[14], octets[15],
350 octets[16], octets[17], octets[18],
351 ]);
352 Some(IpAddr::V6(ip))
353 }
354 (0, 1) => {
355 let ip = Ipv4Addr::from([octets[3], octets[4], octets[5], octets[6]]);
357 Some(IpAddr::V4(ip))
358 }
359 _ => None,
360 }
361 }
362
363 pub fn get_rfc1277_socket(&self) -> Option<Rfc1277SocketInfo> {
371 let octets = self.get_octets();
372 if !octets.starts_with(RFC_1277_PREFIX.as_slice()) {
373 return None;
374 }
375 let dsp = &octets[RFC_1277_PREFIX.len() + 1..];
376 if dsp.len() < 6 {
377 return None;
378 }
379 let mut bcd = BCDDigitsIter::new(dsp, false, false, false, false);
380 let oct1digs = [bcd.next()? + 0x30, bcd.next()? + 0x30, bcd.next()? + 0x30];
381 let oct2digs = [bcd.next()? + 0x30, bcd.next()? + 0x30, bcd.next()? + 0x30];
382 let oct3digs = [bcd.next()? + 0x30, bcd.next()? + 0x30, bcd.next()? + 0x30];
383 let oct4digs = [bcd.next()? + 0x30, bcd.next()? + 0x30, bcd.next()? + 0x30];
384 let oct1str = unsafe { str::from_utf8_unchecked(oct1digs.as_slice()) };
385 let oct2str = unsafe { str::from_utf8_unchecked(oct2digs.as_slice()) };
386 let oct3str = unsafe { str::from_utf8_unchecked(oct3digs.as_slice()) };
387 let oct4str = unsafe { str::from_utf8_unchecked(oct4digs.as_slice()) };
388 let oct1: u8 = oct1str.parse().ok()?;
389 let oct2: u8 = oct2str.parse().ok()?;
390 let oct3: u8 = oct3str.parse().ok()?;
391 let oct4: u8 = oct4str.parse().ok()?;
392 let ip = Ipv4Addr::new(oct1, oct2, oct3, oct4);
393 if dsp.len() < 9 {
394 return Some((
395 octets[5],
396 SocketAddrV4::new(ip, ITOT_OVER_IPV4_DEFAULT_PORT),
397 DEFAULT_ITOT_TRANSPORT_SET,
398 ));
399 }
400 let portstr = [
401 bcd.next()? + 0x30,
402 bcd.next()? + 0x30,
403 bcd.next()? + 0x30,
404 bcd.next()? + 0x30,
405 bcd.next()? + 0x30,
406 ];
407 let portstr = unsafe { str::from_utf8_unchecked(portstr.as_slice()) };
408 let port: u16 = portstr.parse().ok()?;
409 if dsp.len() < 11 {
410 return Some((
411 octets[5],
412 SocketAddrV4::new(ip, ITOT_OVER_IPV4_DEFAULT_PORT),
413 DEFAULT_ITOT_TRANSPORT_SET,
414 ));
415 }
416 let tsetstr = [
417 bcd.next()? + 0x30,
418 bcd.next()? + 0x30,
419 bcd.next()? + 0x30,
420 bcd.next()? + 0x30,
421 bcd.next()? + 0x30,
422 ];
423 let tsetstr = unsafe { str::from_utf8_unchecked(tsetstr.as_slice()) };
424 let tset: Rfc1277TransportSet = tsetstr.parse().ok()?;
425 Some((octets[5], SocketAddrV4::new(ip, port), tset))
426 }
427
428 pub fn from_ip(ip: &IpAddr) -> Self {
430 match ip {
431 IpAddr::V4(v4) => X213NetworkAddress::from_ipv4(v4),
432 IpAddr::V6(v6) => X213NetworkAddress::from_ipv6(v6),
433 }
434 }
435
436 pub fn from_ipv4(ip: &Ipv4Addr) -> Self {
438 let mut out: [u8; 22] = [0; 22];
439 out[0..3].copy_from_slice(&[AFI_IANA_ICP_BIN, 0, 1]);
440 out[3..7].copy_from_slice(ip.octets().as_slice());
441 return X213NetworkAddress::Inline((20, out));
443 }
444
445 pub fn from_ipv6(ip: &Ipv6Addr) -> Self {
447 let mut out: [u8; 22] = [0; 22];
448 out[0..3].copy_from_slice(&[AFI_IANA_ICP_BIN, 0, 0]);
449 out[3..19].copy_from_slice(ip.octets().as_slice());
450 return X213NetworkAddress::Inline((20, out));
452 }
453
454 #[cfg(feature = "alloc")]
456 pub fn from_itot_url(url: &str) -> Self {
457 let mut out: Vec<u8> = Vec::with_capacity(3 + url.len());
458 out.extend(&[AFI_URL, 0, 0]);
459 out.extend(url.as_bytes());
460 return X213NetworkAddress::Heap(out);
461 }
462
463 #[cfg(feature = "alloc")]
465 pub fn from_non_osi_url(url: &str) -> Self {
466 let mut out: Vec<u8> = Vec::with_capacity(3 + url.len());
467 out.extend(&[AFI_URL, 0, 1]);
468 out.extend(url.as_bytes());
469 return X213NetworkAddress::Heap(out);
470 }
471
472 pub fn from_socket_addr_v4(network: u8, addr: &SocketAddrV4, tset: Option<u16>) -> Self {
476 let mut out: [u8; 22] = [0; 22];
477 out[0..5].copy_from_slice(RFC_1277_PREFIX.as_slice());
478 out[5] = network;
479 let mut bcd_buf = BCDBuffer::new();
480 addr.ip()
481 .octets()
482 .map(|o| u8_to_decimal_bytes(o))
483 .iter()
484 .for_each(|dec_oct| bcd_buf.push_ascii_bytes(dec_oct.as_slice()));
485 let port = addr.port();
486 if port != ITOT_OVER_IPV4_DEFAULT_PORT
487 || tset.is_some_and(|t| t != DEFAULT_ITOT_TRANSPORT_SET)
488 {
489 let port_str = u16_to_decimal_bytes(port);
490 bcd_buf.push_ascii_bytes(port_str.as_slice());
491 if let Some(tset) = tset {
492 let tset_str = u16_to_decimal_bytes(tset);
493 bcd_buf.push_ascii_bytes(tset_str.as_slice());
494 } else {
495 bcd_buf.push_nybble(0xF);
496 }
497 }
498 let bcd_len = bcd_buf.len_in_bytes();
499 debug_assert_eq!(bcd_len, bcd_buf.as_ref().len());
500 debug_assert!(bcd_len < 19);
501 out[6..6 + bcd_len].copy_from_slice(bcd_buf.as_ref());
502 X213NetworkAddress::Inline((6 + bcd_len as u8, out))
503 }
504
505 #[cfg(feature = "alloc")]
514 pub fn to_ns_string(&self) -> String {
515 let octets = self.get_octets();
516 let len = 3 + (octets.len() << 1);
517 let mut out: Vec<u8> = Vec::with_capacity(len);
518 out.extend(b"NS+");
519 unsafe {
521 out.set_len(len);
522 }
523 faster_hex::hex_encode(octets, &mut out[3..]).expect("hex output buffer mis-sized");
524 unsafe { String::from_utf8_unchecked(out) }
525 }
526
527 pub fn fmt_as_ns_string(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
536 f.write_str("NS+")?;
537 for byte in self.get_octets() {
538 f.write_fmt(format_args!("{:02X}", *byte))?;
539 }
540 Ok(())
541 }
542}
543
544fn validate_decimal(bytes: &[u8]) -> bool {
545 for byte in bytes {
546 if (byte & 0b0000_1111) > 9 {
547 return false;
548 }
549 if (byte & 0b1111_0000) > 0b1001_0000 {
550 return false;
551 }
552 }
553 true
554}
555
556fn validate_raw_nsap<'a>(octets: &'a [u8]) -> Result<(), NAddressParseError> {
557 let len = octets.len();
558 if len < 2 {
559 return Err(NAddressParseError::TooShort);
561 }
562 if octets[0] != AFI_URL && len > 20 {
567 return Err(NAddressParseError::TooLong);
568 }
569
570 match octets[0] {
571 crate::AFI_URL => {
572 if len > 248 {
573 return Err(NAddressParseError::TooLong);
574 }
575 if len <= 5 {
576 return Err(NAddressParseError::TooShort);
578 }
579 if !validate_decimal(&octets[1..3]) {
580 return Err(NAddressParseError::NonDigitsInIDI);
581 }
582 }
583 crate::AFI_IANA_ICP_BIN => {
584 if len > 20 {
585 return Err(NAddressParseError::TooLong);
586 }
587 if len < 20 {
588 return Err(NAddressParseError::TooShort);
589 }
590 if !validate_decimal(&octets[1..3]) {
591 return Err(NAddressParseError::NonDigitsInIDI);
592 }
593 }
594 _ => (),
595 };
596
597 if len >= RFC_1277_PREFIX.len() + 7 && octets.starts_with(RFC_1277_PREFIX.as_slice()) {
598 match octets[RFC_1277_PREFIX.len()] {
599 crate::data::RFC_1277_WELL_KNOWN_NETWORK_DARPA_NSF_INTERNET
600 | crate::data::ITU_X519_DSP_PREFIX_LDAP
601 | crate::data::ITU_X519_DSP_PREFIX_IDM_OVER_IPV4
602 => {
604 let end_of_digits = match len {
605 12 => 12,
606 15 => 14,
607 17 => 17,
608 _ => return Err(NAddressParseError::MalformedDSP),
609 };
610 if !validate_decimal(&octets[6..end_of_digits]) {
611 return Err(NAddressParseError::MalformedDSP);
612 }
613 },
614 _ => (),
615 };
616 }
617 Ok(())
618}
619
620impl<'a> TryFrom<&'a [u8]> for X213NetworkAddress<'a> {
621 type Error = NAddressParseError;
622
623 fn try_from(octets: &'a [u8]) -> Result<Self, Self::Error> {
624 validate_raw_nsap(octets)?;
625 Ok(X213NetworkAddress::Borrowed(octets))
626 }
627}
628
629#[cfg(feature = "alloc")]
630impl<'a> TryFrom<Vec<u8>> for X213NetworkAddress<'a> {
631 type Error = NAddressParseError;
632
633 fn try_from(octets: Vec<u8>) -> Result<Self, Self::Error> {
634 validate_raw_nsap(octets.as_ref())?;
635 Ok(X213NetworkAddress::Heap(octets))
636 }
637}
638
639impl<'a> From<&IpAddr> for X213NetworkAddress<'a> {
640 #[inline]
641 fn from(value: &IpAddr) -> Self {
642 X213NetworkAddress::from_ip(value)
643 }
644}
645
646impl<'a> From<&Ipv4Addr> for X213NetworkAddress<'a> {
647 #[inline]
648 fn from(value: &Ipv4Addr) -> Self {
649 X213NetworkAddress::from_ipv4(value)
650 }
651}
652
653impl<'a> From<&Ipv6Addr> for X213NetworkAddress<'a> {
654 #[inline]
655 fn from(value: &Ipv6Addr) -> Self {
656 X213NetworkAddress::from_ipv6(value)
657 }
658}
659
660impl<'a> Display for X213NetworkAddress<'a> {
661 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
662 fmt_naddr(self, f)
663 }
664}
665
666impl<'a> FromStr for X213NetworkAddress<'a> {
667 type Err = RFC1278ParseError;
668
669 #[inline]
670 fn from_str(s: &str) -> Result<Self, RFC1278ParseError> {
671 parse_nsap(s)
672 }
673}
674
675#[cfg(test)]
676mod tests {
677
678 extern crate alloc;
679 use crate::data::{
680 AFI_IANA_ICP_BIN, AFI_ISO_DCC_DEC, AFI_X121_DEC_LEADING_ZERO,
681 RFC_1277_WELL_KNOWN_NETWORK_DARPA_NSF_INTERNET,
682 };
683 use alloc::string::{String, ToString};
684 use alloc::vec::Vec;
685 use alloc::format;
686 use core::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4};
687 use core::str::FromStr;
688
689 use super::X213NetworkAddress;
690
691 use super::data::AFI_F69_DEC_LEADING_ZERO;
692
693 #[test]
694 fn test_display_01() {
695 let input = [
696 0x36u8, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x12, 0x34, 0x56, 0x78, 0x90,
698 ];
699 let addr = X213NetworkAddress::try_from(input.as_slice()).unwrap();
700 let addr_str = addr.to_string();
701 assert_eq!(addr_str, "X121+102030405+d1234567890");
702 }
703
704 #[cfg(feature = "nonstddisplay")]
705 #[test]
706 fn test_display_02_url() {
707 let input = b"\xFF\x00\x01https://wildboarsoftware.com/x500directory";
708 let addr = X213NetworkAddress::try_from(input.as_slice()).unwrap();
709 let addr_str = addr.to_string();
710 assert_eq!(
711 addr_str,
712 "URL+0001+https://wildboarsoftware.com/x500directory"
713 );
714 }
715
716 #[test]
717 fn test_display_02_itot() {
718 let input = &[
719 0x54, 0, 0x72, 0x87, 0x22, 3, 1, 0, 0, 0, 0, 6, 0, 0, 0x90, 0, 2,
720 ];
721 let addr = X213NetworkAddress::try_from(input.as_slice()).unwrap();
722 let addr_str = addr.to_string();
723 assert_eq!(addr_str, "TELEX+00728722+RFC-1006+03+10.0.0.6+9+2");
724 }
725
726 #[cfg(feature = "nonstddisplay")]
727 #[test]
728 fn test_display_03_ip() {
729 let input = &[
730 AFI_IANA_ICP_BIN,
731 0,
732 1,
733 192,
734 168,
735 1,
736 100,
737 0,
738 0,
739 0,
740 0,
741 0,
742 0,
743 0,
744 0,
745 0,
746 0,
747 0,
748 0,
749 0,
750 ];
751 let addr = X213NetworkAddress::try_from(input.as_slice()).unwrap();
752 let addr_str = addr.to_string();
753 assert_eq!(addr_str, "IP4+192.168.1.100");
754 }
755
756 #[test]
757 fn test_get_url() {
758 let input = b"\xFF\x00\x01https://wildboarsoftware.com/x500directory";
759 let addr = X213NetworkAddress::try_from(input.as_slice()).unwrap();
760 assert_eq!(
761 addr.get_url().unwrap(),
762 "https://wildboarsoftware.com/x500directory"
763 );
764 }
765
766 #[test]
767 fn test_from_itot_socket_addr() {
768 let sock = SocketAddrV4::from_str("192.168.1.100:8000").unwrap();
769 let addr = X213NetworkAddress::from_socket_addr_v4(
770 RFC_1277_WELL_KNOWN_NETWORK_DARPA_NSF_INTERNET,
771 &sock,
772 None,
773 );
774 assert_eq!(
776 addr.get_octets(),
777 &[
778 AFI_F69_DEC_LEADING_ZERO, 0x00,
780 0x72,
781 0x87,
782 0x22, 0x03, 0x19,
785 0x21,
786 0x68,
787 0x00,
788 0x11,
789 0x00,
790 0x08,
791 0x00,
792 0x0F,
793 ]
794 );
795 }
796
797 #[cfg(feature = "nonstd")]
798 #[test]
799 fn test_ip_overflow_1() {
800 let input = "IP4+999.999.2.100";
801 let maybe_addr = X213NetworkAddress::from_str(input);
802 assert!(maybe_addr.is_err());
803 }
804
805 #[test]
806 fn test_ip_overflow_2() {
807 let input = "TELEX+00728722+RFC-1006+03+256.0.0.2+9+2";
808 let maybe_addr = X213NetworkAddress::from_str(input);
809 assert!(maybe_addr.is_err());
810 }
811
812 #[test]
813 fn test_ip_overflow_3() {
814 let input = "TELEX+00728722+RFC-1006+03+0.255.255.255+99999+88888";
815 let maybe_addr = X213NetworkAddress::from_str(input);
816 assert!(maybe_addr.is_err());
817 }
818
819 #[test]
820 #[ignore]
821 fn test_ip_overflow_4() {
822 let input: &[u8] = &[
823 0x54, 0x00, 0x72, 0x87, 0x22, 0x03, 0x99, 0x90, 0x00, 0x00, 0x00,
824 0x06, ];
826 let maybe_addr = X213NetworkAddress::try_from(input);
827 assert!(maybe_addr.is_err());
828 }
829
830 #[test]
831 fn test_get_itot_socket_adder() {
832 let input = "TELEX+00728722+RFC-1006+03+255.0.0.2+65535+2";
833 let addr = X213NetworkAddress::from_str(input).unwrap();
834 let (_, sock, _) = addr.get_rfc1277_socket().unwrap();
835 assert_eq!(sock.ip(), &Ipv4Addr::new(255, 0, 0, 2));
836 assert_eq!(sock.port(), 65535);
837 }
838
839 #[test]
841 fn test_idi_digits_x121() {
842 let input = "X121+00123456789+x0824";
843 let addr = X213NetworkAddress::from_str(input).unwrap();
844 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
845 assert_eq!(digits.as_slice(), &[0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
846 }
847
848 #[test]
849 fn test_idi_digits_dcc() {
850 let input = "X121+023+x0824";
851 let addr = X213NetworkAddress::from_str(input).unwrap();
852 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
853 assert_eq!(digits.as_slice(), &[0, 2, 3]);
854 }
855
856 #[test]
858 fn test_idi_digits_telex() {
859 let input = "TELEX+01234+x0824";
860 let addr = X213NetworkAddress::from_str(input).unwrap();
861 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
862 assert_eq!(digits.as_slice(), &[0, 1, 2, 3, 4]);
863 }
864
865 #[test]
867 fn test_idi_digits_pstn() {
868 let input = "PSTN+8883334022+x0824";
869 let addr = X213NetworkAddress::from_str(input).unwrap();
870 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
871 assert_eq!(digits.as_slice(), &[8, 8, 8, 3, 3, 3, 4, 0, 2, 2]);
872 }
873
874 #[test]
876 fn test_idi_digits_idsn() {
877 let input = "ISDN+0018883334022+x0824";
878 let addr = X213NetworkAddress::from_str(input).unwrap();
879 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
880 assert_eq!(digits.as_slice(), &[0, 0, 1, 8, 8, 8, 3, 3, 3, 4, 0, 2, 2]);
881 }
882
883 #[test]
884 fn test_idi_digits_icd() {
885 let input = "ICD+0023+x0824";
886 let addr = X213NetworkAddress::from_str(input).unwrap();
887 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
888 assert_eq!(digits.as_slice(), &[2, 3]);
889 }
890
891 #[cfg(feature = "nonstd")]
892 #[test]
893 fn test_idi_digits_icp() {
894 let input = "ICP+0001+x0824";
895 let addr = X213NetworkAddress::from_str(input).unwrap();
896 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
897 assert_eq!(digits.as_slice(), &[1]);
898 }
899
900 #[cfg(feature = "nonstd")]
901 #[test]
902 fn test_idi_digits_ind() {
903 let input = "IND+0123+x0824";
904 let addr = X213NetworkAddress::from_str(input).unwrap();
905 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
906 assert_eq!(digits.as_slice(), &[1, 2, 3]);
907 }
908
909 #[test]
910 fn test_idi_digits_local() {
911 let input = "LOCAL++x0824";
912 let addr = X213NetworkAddress::from_str(input).unwrap();
913 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
914 assert_eq!(digits.as_slice(), &[]);
915 }
916
917 #[cfg(feature = "nonstd")]
918 #[test]
919 fn test_idi_digits_url() {
920 let input = "URL+0001+x0824";
921 let addr = X213NetworkAddress::from_str(input).unwrap();
922 let digits: Vec<u8> = addr.idi_digits().unwrap().collect();
923 assert_eq!(digits.as_slice(), &[1]);
924 }
925
926 #[test]
927 fn test_dsp_digits_x121() {
928 let input = "X121+00123456789+d2929";
929 let addr = X213NetworkAddress::from_str(input).unwrap();
930 let digits: Vec<u8> = addr.dsp_digits().unwrap().collect();
931 assert_eq!(digits.as_slice(), &[2, 9, 2, 9]);
932 assert_eq!(
933 addr.get_octets(),
934 &[
935 AFI_X121_DEC_LEADING_ZERO,
936 0x11,
937 0x10,
938 0x01,
939 0x23,
940 0x45,
941 0x67,
942 0x89,
943 0x29,
944 0x29,
945 ]
946 );
947 }
948
949 #[test]
950 fn test_dsp_digits_dcc() {
951 let input = "DCC+840+d1298";
952 let addr = X213NetworkAddress::from_str(input).unwrap();
953 let digits: Vec<u8> = addr.dsp_digits().unwrap().collect();
954 assert_eq!(digits.as_slice(), &[1, 2, 9, 8]);
955 assert_eq!(
956 addr.get_octets(),
957 &[AFI_ISO_DCC_DEC, 0x84, 0x01, 0x29, 0x8F,]
958 );
959 }
960
961 #[test]
962 fn test_from_ipv4() {
963 let input = Ipv4Addr::new(192, 168, 1, 255);
964 let addr = X213NetworkAddress::from_ipv4(&input);
965 assert_eq!(
966 addr.get_octets(),
967 &[
968 AFI_IANA_ICP_BIN,
969 0,
970 1, 192,
972 168,
973 1,
974 255, 0,
976 0,
977 0,
978 0,
979 0,
980 0,
981 0,
982 0,
983 0,
984 0,
985 0,
986 0,
987 0, ]
989 );
990 }
991
992 #[test]
993 fn test_from_ipv6() {
994 let input = Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
995 let addr = X213NetworkAddress::from_ipv6(&input);
996 assert_eq!(
997 addr.get_octets(),
998 &[
999 AFI_IANA_ICP_BIN,
1000 0,
1001 0, 0,
1003 1,
1004 0,
1005 2,
1006 0,
1007 3,
1008 0,
1009 4,
1010 0,
1011 5,
1012 0,
1013 6,
1014 0,
1015 7,
1016 0,
1017 8, 0, ]
1020 );
1021 }
1022
1023 #[test]
1024 fn test_from_itot_url() {
1025 let addr = X213NetworkAddress::from_itot_url("https://himom.org");
1026 assert_eq!(addr.get_octets(), b"\xFF\x00\x00https://himom.org");
1027 }
1028
1029 #[test]
1030 fn test_to_ns_string() {
1031 let input = "DCC+840+d1298";
1032 let addr = X213NetworkAddress::from_str(input).unwrap();
1033 assert_eq!(addr.to_ns_string().as_str(), "NS+388401298f");
1034 }
1035
1036 #[test]
1037 fn test_fuzz_failure_01() {
1038 let input: (&[u8], &str, u64, u8) = (
1039 [
1040 0,
1041 0,
1042 ].as_slice(),
1043 "+\0\0+",
1044 82,
1045 0,
1046 );
1047 let (input_b, input_s, input_u64, input_u8) = input;
1048 let _ = X213NetworkAddress::try_from(input_b);
1049 let _ = X213NetworkAddress::from_str(input_s);
1050 let ss1 = [ "X121+", input_s ].join("");
1051 let ss2 = [ "DCC+", input_s ].join("");
1052 let ss3 = [ "TELEX+", input_s ].join("");
1053 let ss4 = [ "PSTN+", input_s ].join("");
1054 let ss5 = [ "ISDN+", input_s ].join("");
1055 let ss6 = [ "ICD+", input_s ].join("");
1056 let ss7 = [ "ICP+", input_s ].join("");
1057 let ss8 = [ "IND+", input_s ].join("");
1058 let ss9 = [ "LOCAL+", input_s ].join("");
1059 let ss10 = [ "URL+", input_s ].join("");
1060 let ss11 = [ "IP4+", input_s ].join("");
1061 let ss12 = [ "IP6+", input_s ].join("");
1062
1063 let mut hexstr: Vec<u8> = Vec::with_capacity(input_b.len() << 1);
1064 unsafe { hexstr.set_len(input_b.len() << 1) };
1065 faster_hex::hex_encode(input_b, hexstr.as_mut_slice()).unwrap();
1066 let hexstr = unsafe { String::from_utf8_unchecked(hexstr) };
1067 let sb1 = [ "X121+", hexstr.as_str() ].join("");
1068 let sb2 = [ "DCC+", hexstr.as_str() ].join("");
1069 let sb3 = [ "TELEX+", hexstr.as_str() ].join("");
1070 let sb4 = [ "PSTN+", hexstr.as_str() ].join("");
1071 let sb5 = [ "ISDN+", hexstr.as_str() ].join("");
1072 let sb6 = [ "ICD+", hexstr.as_str() ].join("");
1073 let sb7 = [ "ICP+", hexstr.as_str() ].join("");
1074 let sb8 = [ "IND+", hexstr.as_str() ].join("");
1075 let sb9 = [ "LOCAL+", hexstr.as_str() ].join("");
1076 let sb10 = [ "URL+", hexstr.as_str() ].join("");
1077 let sb11 = [ "IP4+", hexstr.as_str() ].join("");
1078 let sb12 = [ "IP6+", hexstr.as_str() ].join("");
1079
1080 let url = format!("URL+{}+{}", input_u8, input_s);
1081 let x25_1 = format!("TELEX+00728722+X.25(80)+02+{}", input_u64);
1082 let x25_2 = format!("TELEX+00728722+X.25(80)+02+{}+CUDF+{}", input_u64, input_u8);
1083 let x25_3 = format!("TELEX+00728722+X.25(80)+{}+{}+CUDF+{}", input_u8, input_u8, input_u8);
1084 let itot1 = format!("TELEX+00728722+RFC-1006+{}+{}.{}.{}.{}", input_u8, input_u8, input_u8, input_u8, input_u8);
1085 let itot2 = format!("TELEX+00728722+RFC-1006+03+2.3.4.5+{}", input_u8);
1086 let itot3 = format!("TELEX+00728722+RFC-1006+03+2.3.4.5+{}+{}", input_u8, input_u8);
1087 let x121 = format!("X121+{}", input_u64);
1088
1089 let _ = X213NetworkAddress::from_str(ss1.as_str());
1090 let _ = X213NetworkAddress::from_str(ss2.as_str());
1091 let _ = X213NetworkAddress::from_str(ss3.as_str());
1092 let _ = X213NetworkAddress::from_str(ss4.as_str());
1093 let _ = X213NetworkAddress::from_str(ss5.as_str());
1094 let _ = X213NetworkAddress::from_str(ss6.as_str());
1095 let _ = X213NetworkAddress::from_str(ss7.as_str());
1096 let _ = X213NetworkAddress::from_str(ss8.as_str());
1097 let _ = X213NetworkAddress::from_str(ss9.as_str());
1098 let _ = X213NetworkAddress::from_str(ss10.as_str());
1099 let _ = X213NetworkAddress::from_str(ss11.as_str());
1100 let _ = X213NetworkAddress::from_str(ss12.as_str());
1101 let _ = X213NetworkAddress::from_str(sb1.as_str());
1102 let _ = X213NetworkAddress::from_str(sb2.as_str());
1103 let _ = X213NetworkAddress::from_str(sb3.as_str());
1104 let _ = X213NetworkAddress::from_str(sb4.as_str());
1105 let _ = X213NetworkAddress::from_str(sb5.as_str());
1106 let _ = X213NetworkAddress::from_str(sb6.as_str());
1107 let _ = X213NetworkAddress::from_str(sb7.as_str());
1108 let _ = X213NetworkAddress::from_str(sb8.as_str());
1109 let _ = X213NetworkAddress::from_str(sb9.as_str());
1110 let _ = X213NetworkAddress::from_str(sb10.as_str());
1111 let _ = X213NetworkAddress::from_str(sb11.as_str());
1112 let _ = X213NetworkAddress::from_str(sb12.as_str());
1113 let _ = X213NetworkAddress::from_str(url.as_str());
1114 let _ = X213NetworkAddress::from_str(x25_1.as_str());
1115 let _ = X213NetworkAddress::from_str(x25_2.as_str());
1116 let _ = X213NetworkAddress::from_str(x25_3.as_str());
1117 let _ = X213NetworkAddress::from_str(itot1.as_str());
1118 let _ = X213NetworkAddress::from_str(itot2.as_str());
1119 let _ = X213NetworkAddress::from_str(itot3.as_str());
1120 let _ = X213NetworkAddress::from_str(x121.as_str());
1121 }
1122
1123}