1use super::*;
2
3use crate::error::strings::InvalidIso646Character;
4use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
5use once_cell::race::OnceBox;
6
7#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
9pub struct Ia5String(Vec<u8>);
10static CHARACTER_MAP: OnceBox<alloc::collections::BTreeMap<u32, u32>> = OnceBox::new();
11static INDEX_MAP: OnceBox<alloc::collections::BTreeMap<u32, u32>> = OnceBox::new();
12
13impl Ia5String {
14 pub fn from_iso646_bytes(bytes: &[u8]) -> Result<Self, InvalidIso646Character> {
15 bytes.iter().try_for_each(|byte| {
16 if Self::CHARACTER_SET.contains(&(*byte as u32)) {
17 Ok(())
18 } else {
19 Err(InvalidIso646Character { character: *byte })
20 }
21 })?;
22
23 Ok(Self(bytes.to_owned()))
24 }
25
26 pub fn as_iso646_bytes(&self) -> &[u8] {
27 &self.0
28 }
29}
30
31impl core::fmt::Display for Ia5String {
32 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33 f.write_str(&String::from_utf8(self.as_iso646_bytes().to_owned()).unwrap())
34 }
35}
36
37impl AsnType for Ia5String {
38 const TAG: Tag = Tag::IA5_STRING;
39}
40
41impl Encode for Ia5String {
42 fn encode_with_tag_and_constraints<E: Encoder>(
43 &self,
44 encoder: &mut E,
45 tag: Tag,
46 constraints: Constraints,
47 ) -> Result<(), E::Error> {
48 encoder.encode_ia5_string(tag, constraints, self).map(drop)
49 }
50}
51
52impl Decode for Ia5String {
53 fn decode_with_tag_and_constraints<D: Decoder>(
54 decoder: &mut D,
55 tag: Tag,
56 constraints: Constraints,
57 ) -> Result<Self, D::Error> {
58 decoder.decode_ia5_string(tag, constraints)
59 }
60}
61
62impl TryFrom<alloc::string::String> for Ia5String {
63 type Error = InvalidIso646Character;
64
65 fn try_from(value: alloc::string::String) -> Result<Self, Self::Error> {
66 Self::from_iso646_bytes(value.as_bytes())
67 }
68}
69
70impl TryFrom<&'_ str> for Ia5String {
71 type Error = InvalidIso646Character;
72
73 fn try_from(value: &str) -> Result<Self, Self::Error> {
74 Self::from_iso646_bytes(value.as_bytes())
75 }
76}
77
78impl TryFrom<alloc::vec::Vec<u8>> for Ia5String {
79 type Error = InvalidIso646Character;
80
81 fn try_from(value: alloc::vec::Vec<u8>) -> Result<Self, Self::Error> {
82 Self::from_iso646_bytes(&value)
83 }
84}
85
86impl TryFrom<&'_ [u8]> for Ia5String {
87 type Error = InvalidIso646Character;
88
89 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
90 Self::from_iso646_bytes(value)
91 }
92}
93
94impl TryFrom<bytes::Bytes> for Ia5String {
95 type Error = InvalidIso646Character;
96
97 fn try_from(value: bytes::Bytes) -> Result<Self, Self::Error> {
98 Self::try_from(&*value)
99 }
100}
101
102impl From<Ia5String> for bytes::Bytes {
103 fn from(value: Ia5String) -> Self {
104 value.0.into()
105 }
106}
107
108impl From<Ia5String> for alloc::string::String {
109 fn from(value: Ia5String) -> Self {
110 Self::from_utf8(value.as_iso646_bytes().to_owned()).unwrap()
111 }
112}
113
114impl super::StaticPermittedAlphabet for Ia5String {
115 const CHARACTER_SET: &'static [u32] = &[
116 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
117 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
118 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
119 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
120 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
121 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
122 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
123 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
124 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
125 ];
126
127 fn chars(&self) -> Box<dyn Iterator<Item = u32> + '_> {
128 Box::from(self.0.iter().map(|byte| *byte as u32))
129 }
130
131 fn push_char(&mut self, ch: u32) {
132 debug_assert!(
133 Self::CHARACTER_SET.contains(&ch),
134 "{} not in character set",
135 ch
136 );
137 self.0.push(ch as u8);
138 }
139
140 fn index_map() -> &'static alloc::collections::BTreeMap<u32, u32> {
141 INDEX_MAP.get_or_init(Self::build_index_map)
142 }
143
144 fn character_map() -> &'static alloc::collections::BTreeMap<u32, u32> {
145 CHARACTER_MAP.get_or_init(Self::build_character_map)
146 }
147}