simple_dns/dns/
character_string.rs1use std::{borrow::Cow, convert::TryFrom, fmt::Display};
2
3use crate::SimpleDnsError;
4
5use super::{WireFormat, MAX_CHARACTER_STRING_LENGTH};
6
7#[derive(PartialEq, Eq, Hash, Clone)]
14pub struct CharacterString<'a> {
15 pub(crate) data: Cow<'a, [u8]>,
16}
17
18impl<'a> CharacterString<'a> {
19 pub fn new(data: &'a [u8]) -> crate::Result<Self> {
21 Self::internal_new(Cow::Borrowed(data))
22 }
23
24 fn internal_new(data: Cow<'a, [u8]>) -> crate::Result<Self> {
25 if data.len() > MAX_CHARACTER_STRING_LENGTH {
26 return Err(SimpleDnsError::InvalidCharacterString);
27 }
28 Ok(Self { data })
29 }
30
31 pub fn into_owned<'b>(self) -> CharacterString<'b> {
33 CharacterString {
34 data: self.data.into_owned().into(),
35 }
36 }
37}
38
39impl<'a> TryFrom<CharacterString<'a>> for String {
40 type Error = crate::SimpleDnsError;
41
42 fn try_from(val: CharacterString<'a>) -> Result<Self, Self::Error> {
43 match String::from_utf8(val.data.into()) {
44 Ok(s) => Ok(s),
45 Err(e) => Err(SimpleDnsError::InvalidUtf8String(e)),
46 }
47 }
48}
49
50impl<'a> WireFormat<'a> for CharacterString<'a> {
51 fn parse(data: &'a [u8], position: &mut usize) -> crate::Result<Self>
52 where
53 Self: Sized,
54 {
55 let length = data[*position] as usize;
56 if length > MAX_CHARACTER_STRING_LENGTH || length + *position > data.len() {
57 return Err(SimpleDnsError::InvalidCharacterString);
58 }
59
60 let data = &data[*position + 1..*position + 1 + length];
61 *position += length + 1;
62
63 Ok(Self {
64 data: Cow::Borrowed(data),
65 })
66 }
67
68 fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
69 out.write_all(&[self.data.len() as u8])?;
70 out.write_all(&self.data)
71 .map_err(crate::SimpleDnsError::from)
72 }
73
74 fn len(&self) -> usize {
75 self.data.len() + 1
76 }
77}
78
79impl<'a> TryFrom<&'a str> for CharacterString<'a> {
80 type Error = crate::SimpleDnsError;
81
82 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
83 CharacterString::internal_new(Cow::Borrowed(value.as_bytes()))
84 }
85}
86
87impl<'a> TryFrom<String> for CharacterString<'a> {
88 type Error = crate::SimpleDnsError;
89
90 fn try_from(value: String) -> Result<Self, Self::Error> {
91 CharacterString::internal_new(Cow::Owned(value.as_bytes().into()))
92 }
93}
94
95impl<'a> Display for CharacterString<'a> {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 let s = std::str::from_utf8(&self.data).unwrap();
98 f.write_str(s)
99 }
100}
101
102impl<'a> std::fmt::Debug for CharacterString<'a> {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 f.debug_struct("CharacterString")
105 .field("data", &self.to_string())
106 .finish()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use std::{
113 collections::hash_map::DefaultHasher,
114 hash::{Hash, Hasher},
115 };
116
117 use super::*;
118
119 #[test]
120 fn construct_valid_character_string() {
121 assert!(CharacterString::new(b"Iamvalid").is_ok());
122 assert!(CharacterString::new(br#""I am valid""#).is_ok());
123 assert!(CharacterString::new(br#""I am \" also valid""#).is_ok());
124 assert!(CharacterString::new(b"I am valid").is_ok());
125
126 let long_string = [0u8; 300];
127 assert!(CharacterString::new(&long_string).is_err());
128 }
129
130 #[test]
131 fn parse() {
132 let c_string = CharacterString::parse(b"\x0esome_long_text", &mut 0);
133 assert!(c_string.is_ok());
134 let c_string = c_string.unwrap();
135 assert_eq!(15, c_string.len());
136 assert_eq!("some_long_text", c_string.to_string());
137 }
138
139 #[test]
140 fn append_to_vec() {
141 let mut out = Vec::new();
142 let c_string = CharacterString::new("some_long_text".as_bytes()).unwrap();
143 c_string.write_to(&mut out).unwrap();
144
145 assert_eq!(b"\x0esome_long_text", &out[..]);
146 }
147
148 #[test]
149 fn eq() {
150 let a = CharacterString::new(b"text").unwrap();
151 let b = CharacterString::new(b"text").unwrap();
152
153 assert_eq!(a, b);
154 assert_eq!(get_hash(a), get_hash(b));
155 }
156
157 fn get_hash(string: CharacterString) -> u64 {
158 let mut hasher = DefaultHasher::default();
159 string.hash(&mut hasher);
160 hasher.finish()
161 }
162}