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