1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "x690"), no_std)]
3#![allow(non_snake_case)]
4
5#[cfg(feature = "x690")]
6pub mod asn1;
7use core::fmt::{Display, Write};
8use core::iter::{FusedIterator, Iterator};
9use core::str::FromStr;
10use core::write;
11#[cfg(feature = "x690")]
12use x690::X690Element;
13
14extern crate alloc;
15use alloc::collections::BTreeSet;
16use alloc::vec::Vec;
17
18pub type Selector = Vec<u8>;
33
34#[derive(Debug, Clone)]
50pub struct PresentationAddress {
51 pub pSelector: Option<Selector>,
53 pub sSelector: Option<Selector>,
55 pub tSelector: Option<Selector>,
57 pub nAddresses: Vec<Selector>,
59 #[cfg(feature = "x690")]
60 pub _unrecognized: Vec<X690Element>,
61}
62
63impl PresentationAddress {
64 #[inline]
66 pub fn new(
67 pSelector: Option<Selector>,
68 sSelector: Option<Selector>,
69 tSelector: Option<Selector>,
70 nAddresses: Vec<Selector>,
71 #[cfg(feature = "x690")] _unrecognized: Vec<X690Element>,
72 ) -> Self {
73 PresentationAddress {
74 pSelector,
75 sSelector,
76 tSelector,
77 nAddresses,
78 #[cfg(feature = "x690")]
79 _unrecognized,
80 }
81 }
82
83 pub fn is_naively_subset_of(&self, other: &Self) -> bool {
93 if self.pSelector.as_ref() != other.pSelector.as_ref() {
94 return false;
95 }
96 if self.sSelector.as_ref() != other.sSelector.as_ref() {
97 return false;
98 }
99 if self.tSelector.as_ref() != other.tSelector.as_ref() {
100 return false;
101 }
102 if self.nAddresses.len() == 0 {
103 return false;
104 }
105 if self.nAddresses.len() > other.nAddresses.len() {
107 return false;
109 }
110 let othern = BTreeSet::from_iter(other.nAddresses.iter());
111 for naddr in self.nAddresses.iter() {
112 if !othern.contains(naddr) {
113 return false;
114 }
115 }
116 true
117 }
118
119 pub fn is_naively_exactly(&self, other: &Self) -> bool {
129 if self.pSelector.as_ref() != other.pSelector.as_ref() {
130 return false;
131 }
132 if self.sSelector.as_ref() != other.sSelector.as_ref() {
133 return false;
134 }
135 if self.tSelector.as_ref() != other.tSelector.as_ref() {
136 return false;
137 }
138 if self.nAddresses.len() == 0 {
139 return false;
141 }
142 if self.nAddresses.len() != other.nAddresses.len() {
144 return false;
145 }
146 let selfn = BTreeSet::from_iter(self.nAddresses.iter());
147 let mut othern = BTreeSet::from_iter(other.nAddresses.iter());
148 if selfn.len() != othern.len() {
150 return false;
151 }
152 for naddr in selfn.iter() {
153 if !othern.remove(naddr) {
154 return false;
155 }
156 }
157 true
158 }
159}
160
161fn sel_str_to_bytes(s: &str) -> Result<Vec<u8>, ()> {
162 if s.len() == 1 {
164 return Err(());
165 }
166 match s.chars().next() {
167 Some('"') => {
168 if let Some(end_quote_idx) = s[1..].find('"') {
170 let inner_str = &s[1..=end_quote_idx];
171 Ok(inner_str.as_bytes().to_vec())
172 } else {
173 Err(())
174 }
175 }
176 Some('\'') => {
177 if let Some(end_quote_idx) = s[1..].find('\'') {
179 let inner_str = &s[1..=end_quote_idx];
180 let bytelen = inner_str.len() >> 1;
181 let mut bytes: Vec<u8> = Vec::with_capacity(bytelen);
182 unsafe {
183 bytes.set_len(bytelen);
184 }
185 faster_hex::hex_decode(inner_str.as_bytes(), bytes.as_mut_slice())
186 .map_err(|_| ())?;
187 Ok(bytes)
188 } else {
189 Err(())
190 }
191 }
192 Some('#') => {
193 let sel = u16::from_str(&s[1..]).map_err(|_| ())?;
195 Ok(sel.to_be_bytes().to_vec())
196 }
197 None => Ok(Vec::new()),
198 _ => Err(()),
199 }
200}
201
202fn naddr_str_to_bytes(s: &str) -> Result<Vec<u8>, ()> {
203 #[cfg(feature = "nsap-address")]
204 {
205 let nsap = nsap_address::X213NetworkAddress::from_str(s).map_err(|_| ())?;
206 Ok(nsap.get_octets().to_vec())
207 }
208 #[cfg(not(feature = "nsap-address"))]
209 {
210 if s.starts_with("NS+") {
211 let bytelen = s[3..].len() >> 1;
212 let mut bytes: Vec<u8> = Vec::with_capacity(bytelen);
213 unsafe {
214 bytes.set_len(bytelen);
215 }
216 faster_hex::hex_decode(s[3..].as_bytes(), bytes.as_mut_slice()).map_err(|_| ())?;
217 Ok(bytes)
218 } else {
219 Err(())
221 }
222 }
223}
224
225impl FromStr for PresentationAddress {
226 type Err = ();
227
228 fn from_str(s: &str) -> Result<Self, Self::Err> {
231 let mut selectors = RFC1278SelectorIterator::new(s);
232 let s1 = selectors.next();
233 let s2 = selectors.next();
234 let s3 = selectors.next();
235 debug_assert!(selectors.next().is_none());
236 let (psel, ssel, tsel) = if s3.is_some() {
237 (s1, s2, s3)
238 } else if s2.is_some() {
239 (None, s1, s2)
240 } else if s1.is_some() {
241 (None, None, s1)
242 } else {
243 (None, None, None)
244 };
245
246 let psel = match psel {
247 Some(sel) => Some(sel_str_to_bytes(sel)?),
248 None => None,
249 };
250 let ssel = match ssel {
251 Some(sel) => Some(sel_str_to_bytes(sel)?),
252 None => None,
253 };
254 let tsel = match tsel {
255 Some(sel) => Some(sel_str_to_bytes(sel)?),
256 None => None,
257 };
258
259 let nsaps_len = selectors.remainder().split('_').count();
260 let mut nsaps: Vec<Vec<u8>> = Vec::with_capacity(nsaps_len + 1);
261 for nsap in selectors.remainder().split('_') {
262 nsaps.push(naddr_str_to_bytes(nsap)?);
263 }
264 #[cfg(feature = "x690")]
265 {
266 Ok(PresentationAddress::new(psel, ssel, tsel, nsaps, vec![]))
267 }
268 #[cfg(not(feature = "x690"))]
269 {
270 Ok(PresentationAddress::new(psel, ssel, tsel, nsaps))
271 }
272 }
273}
274
275fn print_selector(sel: &[u8], f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280 let len = sel.len();
281 if len > 2 && len <= 16 && sel.iter().all(|b| is_other_char(*b as char)) {
300 let selstr = unsafe { str::from_utf8_unchecked(sel) };
301 f.write_char('"')?;
302 f.write_str(selstr)?;
303 return f.write_char('"');
304 }
305 f.write_char('\'')?;
306 for byte in sel {
307 write!(f, "{:02X}", *byte)?;
308 }
309 f.write_char('\'')?;
310 f.write_char('H')
311}
312
313fn print_ns_string(naddr: &[u8], f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
314 f.write_str("NS+")?;
315 for byte in naddr {
316 f.write_fmt(format_args!("{:02X}", *byte))?;
317 }
318 Ok(())
319}
320
321fn print_naddr(naddr: &[u8], f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
322 #[cfg(feature = "nsap-address")]
323 {
324 match nsap_address::X213NetworkAddress::try_from(naddr) {
325 Ok(nsap) => nsap.fmt(f),
326 Err(_) => print_ns_string(naddr, f),
327 }
328 }
329 #[cfg(not(feature = "nsap-address"))]
330 {
331 print_ns_string(naddr, f)
332 }
333}
334
335impl Display for PresentationAddress {
336 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
339 let mut print_selectors: bool = false;
340 if let Some(sel) = self.pSelector.as_deref() {
341 print_selector(sel, f)?;
342 f.write_char('/')?;
343 print_selectors = true;
344 }
345 if let Some(sel) = self.sSelector.as_deref() {
346 print_selector(sel, f)?;
347 f.write_char('/')?;
348 print_selectors = true;
349 } else if print_selectors {
350 f.write_char('/')?;
351 }
352 if let Some(sel) = self.tSelector.as_deref() {
353 print_selector(sel, f)?;
354 f.write_char('/')?;
355 } else if print_selectors {
356 f.write_char('/')?;
357 }
358 for naddr in self.nAddresses.iter() {
359 print_naddr(naddr.as_slice(), f)?;
360 }
361 Ok(())
362 }
363}
364
365#[inline]
367const fn is_other_char(c: char) -> bool {
368 c.is_ascii_alphanumeric() || c == '+' || c == '-' || c == '.'
369}
370
371pub struct RFC1278SelectorIterator<'a> {
380 s: &'a str,
381 selectors_read: u8,
382}
383
384impl<'a> RFC1278SelectorIterator<'a> {
385 pub fn new(s: &'a str) -> Self {
387 RFC1278SelectorIterator {
388 s,
389 selectors_read: 0,
390 }
391 }
392
393 pub fn remainder(&self) -> &'a str {
399 self.s
400 }
401}
402
403impl<'a> Iterator for RFC1278SelectorIterator<'a> {
410 type Item = &'a str;
411
412 fn next(&mut self) -> Option<Self::Item> {
413 if self.selectors_read >= 3 {
414 return None;
415 }
416 let first_char = self.s.chars().next()?;
417 match first_char {
418 '\'' | '"' | '#' => {
419 let (sel, rest) = self.s.split_once('/')?;
421 self.s = rest;
422 self.selectors_read += 1;
423 Some(sel)
424 }
425 _ => None,
427 }
428 }
429}
430
431impl<'a> FusedIterator for RFC1278SelectorIterator<'a> {}
432
433#[cfg(test)]
434mod tests {
435
436 use core::str::FromStr;
437
438 extern crate alloc;
439 use alloc::string::ToString;
440 use alloc::vec;
441
442 use super::{PresentationAddress, RFC1278SelectorIterator};
443
444 #[test]
445 fn test_from_sel_iter() {
446 let input = if cfg!(feature = "nsap-address") {
447 "'01020304'H/\"HIMOM\"/#65535/TELEX+00728722+RFC-1006+03+10.0.0.6+9+2"
448 } else {
449 "'01020304'H/\"HIMOM\"/#65535/NS+5400728722030100000000060000900002"
450 };
451 let mut seliter = RFC1278SelectorIterator::new(input);
452 let s1 = seliter.next();
453 let s2 = seliter.next();
454 let s3 = seliter.next();
455 let s4 = seliter.next();
456 let s5 = seliter.next();
457 assert_eq!(s1, Some("'01020304'H"));
458 assert_eq!(s2, Some("\"HIMOM\""));
459 assert_eq!(s3, Some("#65535"));
460 assert_eq!(s4, None);
461 assert_eq!(s5, None);
462 }
463
464 #[test]
465 fn test_from_str_01() {
466 let input = if cfg!(feature = "nsap-address") {
467 "'01020304'H/\"HIMOM\"/#65534/TELEX+00728722+RFC-1006+03+10.0.0.6+9+2"
468 } else {
469 "'01020304'H/\"HIMOM\"/#65534/NS+5400728722030100000000060000900002"
470 };
471 let paddr = PresentationAddress::from_str(input).unwrap();
472 assert_eq!(
473 paddr.pSelector.unwrap().as_slice(),
474 [1u8, 2, 3, 4,].as_slice()
475 );
476 assert_eq!(paddr.sSelector.unwrap().as_slice(), b"HIMOM");
477 assert_eq!(
478 paddr.tSelector.unwrap().as_slice(),
479 65534u16.to_be_bytes().as_slice()
480 );
481 assert_eq!(paddr.nAddresses.len(), 1);
482 assert_eq!(
483 paddr.nAddresses[0].as_slice(),
484 &[
485 0x54, 0x00, 0x72, 0x87, 0x22, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
487 0x90, 0x00, 0x02,
488 ]
489 );
490 }
491
492 #[test]
493 fn test_from_str_02() {
494 let input = if cfg!(feature = "nsap-address") {
495 "'01020304'H/\"HIMOM\"/#65534/TELEX+00728722+RFC-1006+03+10.0.0.6+9+2_NS+FF00013132333435"
496 } else {
497 "'01020304'H/\"HIMOM\"/#65534/NS+5400728722030100000000060000900002_NS+FF00013132333435"
498 };
499 let paddr = PresentationAddress::from_str(input).unwrap();
500 assert_eq!(
501 paddr.pSelector.unwrap().as_slice(),
502 [1u8, 2, 3, 4,].as_slice()
503 );
504 assert_eq!(paddr.sSelector.unwrap().as_slice(), b"HIMOM");
505 assert_eq!(
506 paddr.tSelector.unwrap().as_slice(),
507 65534u16.to_be_bytes().as_slice()
508 );
509 assert_eq!(paddr.nAddresses.len(), 2);
510 assert_eq!(
511 paddr.nAddresses[0].as_slice(),
512 &[
513 0x54, 0x00, 0x72, 0x87, 0x22, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
515 0x90, 0x00, 0x02,
516 ]
517 );
518 assert_eq!(
519 paddr.nAddresses[1].as_slice(),
520 &[0xFF, 0x00, 0x01, 0x31, 0x32, 0x33, 0x34, 0x35]
521 );
522 }
523
524 #[test]
525 fn test_display_01() {
526 let paddr = PresentationAddress::new(
527 Some(vec![1, 2, 3, 4]),
528 Some(b"HIMOM".to_vec()),
529 Some(vec![0xFF, 0xFE]),
530 vec![
531 vec![
533 0x54, 0, 0x72, 0x87, 0x22, 3, 1, 0, 0, 0, 0, 6, 0, 0, 0x90, 0, 2,
534 ],
535 ],
536 #[cfg(feature = "x690")]
537 vec![],
538 );
539 let expected = if cfg!(feature = "nsap-address") {
540 "'01020304'H/\"HIMOM\"/'FFFE'H/TELEX+00728722+RFC-1006+03+10.0.0.6+9+2"
541 } else {
542 "'01020304'H/\"HIMOM\"/'FFFE'H/NS+5400728722030100000000060000900002"
543 };
544 assert_eq!(paddr.to_string().as_str(), expected);
545 }
546}