1 use serde::{de, ser};
2 use std::{error::Error, fmt, marker::PhantomData, ops::Deref, str::FromStr};
3 
4 // === CharSetError === //
5 
6 #[derive(Debug)]
7 pub struct CharSetError;
8 
9 impl Error for CharSetError {}
10 
11 impl fmt::Display for CharSetError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>12     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
13         writeln!(f, "invalid charset")
14     }
15 }
16 
17 // === CharSet === //
18 
19 pub trait CharSet {
20     /// Checks whether a sequence is a valid string or not.
check(data: &[u8]) -> bool21     fn check(data: &[u8]) -> bool;
22 }
23 
24 // === RestrictedString === //
25 
26 /// A generic restricted character string.
27 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
28 pub struct RestrictedString<C> {
29     data: Vec<u8>,
30     marker: PhantomData<C>,
31 }
32 
33 impl<C: CharSet> RestrictedString<C> {
34     /// Create a new RestrictedString without CharSet validation.
35     ///
36     /// # Safety
37     ///
38     /// You have to make sure the right CharSet is used.
new_unchecked<V>(data: V) -> Self where V: Into<Vec<u8>>,39     pub unsafe fn new_unchecked<V>(data: V) -> Self
40     where
41         V: Into<Vec<u8>>,
42     {
43         RestrictedString {
44             data: data.into(),
45             marker: PhantomData,
46         }
47     }
48 
new<V>(data: V) -> Result<Self, CharSetError> where V: Into<Vec<u8>>,49     pub fn new<V>(data: V) -> Result<Self, CharSetError>
50     where
51         V: Into<Vec<u8>>,
52     {
53         let data = data.into();
54         if !C::check(&data) {
55             return Err(CharSetError);
56         };
57         Ok(RestrictedString {
58             data,
59             marker: PhantomData,
60         })
61     }
62 
from_string(s: String) -> Result<Self, CharSetError>63     pub fn from_string(s: String) -> Result<Self, CharSetError> {
64         Self::new(s.into_bytes())
65     }
66 
67     /// Converts into underlying bytes.
into_bytes(self) -> Vec<u8>68     pub fn into_bytes(self) -> Vec<u8> {
69         self.data
70     }
71 
72     /// Returns underlying bytes.
as_bytes(&self) -> &[u8]73     pub fn as_bytes(&self) -> &[u8] {
74         &self.data
75     }
76 }
77 
78 impl<C: CharSet> Deref for RestrictedString<C> {
79     type Target = [u8];
80 
deref(&self) -> &Self::Target81     fn deref(&self) -> &Self::Target {
82         &self.data
83     }
84 }
85 
86 impl<C: CharSet> FromStr for RestrictedString<C> {
87     type Err = CharSetError;
88 
from_str(s: &str) -> Result<Self, CharSetError>89     fn from_str(s: &str) -> Result<Self, CharSetError> {
90         Self::new(s.as_bytes())
91     }
92 }
93 
94 impl<C: CharSet> AsRef<[u8]> for RestrictedString<C> {
as_ref(&self) -> &[u8]95     fn as_ref(&self) -> &[u8] {
96         &self.data
97     }
98 }
99 
100 impl<C: CharSet> fmt::Display for RestrictedString<C> {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result101     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
102         fmt::Display::fmt(&String::from_utf8_lossy(&self.data), fmt)
103     }
104 }
105 
106 impl<C: CharSet> Into<Vec<u8>> for RestrictedString<C> {
into(self) -> Vec<u8>107     fn into(self) -> Vec<u8> {
108         self.into_bytes()
109     }
110 }
111 
112 impl<'de, C> de::Deserialize<'de> for RestrictedString<C>
113 where
114     C: CharSet,
115 {
deserialize<D>(deserializer: D) -> Result<RestrictedString<C>, D::Error> where D: de::Deserializer<'de>,116     fn deserialize<D>(deserializer: D) -> Result<RestrictedString<C>, D::Error>
117     where
118         D: de::Deserializer<'de>,
119     {
120         struct Visitor<C>(std::marker::PhantomData<C>);
121 
122         impl<'de, C> de::Visitor<'de> for Visitor<C>
123         where
124             C: CharSet,
125         {
126             type Value = RestrictedString<C>;
127 
128             fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
129                 formatter.write_str("a valid buffer representing a restricted string")
130             }
131 
132             fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
133             where
134                 E: de::Error,
135             {
136                 self.visit_byte_buf(v.to_vec())
137             }
138 
139             fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
140             where
141                 E: de::Error,
142             {
143                 RestrictedString::new(v).map_err(|_| {
144                     E::invalid_value(
145                         de::Unexpected::Other("invalid charset"),
146                         &"a buffer representing a string using the right charset",
147                     )
148                 })
149             }
150         }
151 
152         deserializer.deserialize_byte_buf(Visitor(std::marker::PhantomData))
153     }
154 }
155 
156 impl<C> ser::Serialize for RestrictedString<C> {
serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error> where S: ser::Serializer,157     fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
158     where
159         S: ser::Serializer,
160     {
161         serializer.serialize_bytes(&self.data)
162     }
163 }
164 
165 // === NumericString === //
166 
167 /// 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, and SPACE
168 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
169 pub struct NumericCharSet;
170 pub type NumericString = RestrictedString<NumericCharSet>;
171 
172 impl CharSet for NumericCharSet {
check(data: &[u8]) -> bool173     fn check(data: &[u8]) -> bool {
174         for &c in data {
175             if c != b' ' && !c.is_ascii_digit() {
176                 return false;
177             }
178         }
179         true
180     }
181 }
182 
183 // === PrintableString === //
184 
185 /// a-z, A-Z, ' () +,-.?:/= and SPACE
186 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
187 pub struct PrintableCharSet;
188 pub type PrintableString = RestrictedString<PrintableCharSet>;
189 
190 impl CharSet for PrintableCharSet {
check(data: &[u8]) -> bool191     fn check(data: &[u8]) -> bool {
192         for &c in data {
193             if !(c.is_ascii_alphanumeric()
194                 || c == b' '
195                 || c == b'\''
196                 || c == b'('
197                 || c == b')'
198                 || c == b'+'
199                 || c == b','
200                 || c == b'-'
201                 || c == b'.'
202                 || c == b'/'
203                 || c == b':'
204                 || c == b'='
205                 || c == b'?')
206             {
207                 return false;
208             }
209         }
210         true
211     }
212 }
213 
214 // === Utf8String === //
215 
216 /// any character from a recognized alphabet (including ASCII control characters)
217 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
218 pub struct Utf8CharSet;
219 pub type Utf8String = RestrictedString<Utf8CharSet>;
220 
221 impl CharSet for Utf8CharSet {
check(data: &[u8]) -> bool222     fn check(data: &[u8]) -> bool {
223         std::str::from_utf8(data).is_ok()
224     }
225 }
226 
227 // === IA5String === //
228 
229 /// First 128 ASCII characters (values from `0x00` to `0x7F`)
230 /// Used to represent ISO 646 (IA5) characters.
231 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
232 pub struct IA5CharSet;
233 pub type IA5String = RestrictedString<IA5CharSet>;
234 
235 impl CharSet for IA5CharSet {
check(data: &[u8]) -> bool236     fn check(data: &[u8]) -> bool {
237         for &c in data {
238             if !c.is_ascii() {
239                 return false;
240             }
241         }
242         true
243     }
244 }
245 
246 #[cfg(test)]
247 mod tests {
248     use super::*;
249 
250     #[test]
valid_printable_string()251     fn valid_printable_string() {
252         PrintableString::from_str("29INRUSAET3snre?:=tanui83  9283019").expect("invalid string");
253     }
254 
255     #[test]
invalid_printable_string()256     fn invalid_printable_string() {
257         assert!(PrintableString::from_str("1224na÷日本語はむずかちー−×—«BUeisuteurnt").is_err());
258     }
259 
260     #[test]
valid_numeric_string()261     fn valid_numeric_string() {
262         NumericString::from_str("2983  9283019").expect("invalid string");
263     }
264 
265     #[test]
invalid_numeric_string()266     fn invalid_numeric_string() {
267         assert!(NumericString::from_str("1224na÷日本語はむずかちー−×—«BUeisuteurnt").is_err());
268     }
269 
270     #[test]
valid_ia5_string()271     fn valid_ia5_string() {
272         IA5String::from_str("BUeisuteurnt").expect("invalid string");
273     }
274 
275     #[test]
invalid_ia5_string()276     fn invalid_ia5_string() {
277         assert!(IA5String::from_str("BUéisuteurnt").is_err());
278     }
279 
280     #[test]
valid_utf8_string()281     fn valid_utf8_string() {
282         Utf8String::from_str("1224na÷日本語はむずかちー−×—«BUeisuteurnt").expect("invalid string");
283     }
284 }
285