tacacs_plus_protocol/
text.rs1use core::fmt;
4
5mod inner;
6use inner::FieldTextInner;
7
8#[cfg(test)]
9mod tests;
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
68pub struct FieldText<'string>(FieldTextInner<'string>);
69
70impl FieldText<'_> {
71 #[cfg(feature = "std")]
74 pub fn from_string_lossy(string: std::string::String) -> FieldText<'static> {
75 use std::string::String;
76
77 let escaped = string
80 .chars()
81 .fold(String::with_capacity(string.len()), |mut result, c| {
82 if char_is_printable_ascii(c) {
83 result.push(c);
84 } else {
85 result.extend(c.escape_default());
86 }
87
88 result
89 });
90
91 FieldText(FieldTextInner::Owned(escaped))
92 }
93
94 #[cfg(feature = "std")]
97 pub fn into_owned(self) -> FieldText<'static> {
98 FieldText(self.0.into_owned())
99 }
100}
101
102impl<'string> FieldText<'string> {
103 pub fn len(&self) -> usize {
105 self.0.len()
106 }
107
108 pub fn as_bytes(&self) -> &[u8] {
110 self.0.as_bytes()
111 }
112
113 pub fn is_empty(&self) -> bool {
115 self.0.is_empty()
116 }
117
118 pub fn contains_any(&self, characters: &[char]) -> bool {
120 self.0.contains(characters)
121 }
122
123 #[cfg(test)]
125 pub(crate) fn assert(string: &str) -> FieldText<'_> {
126 if is_printable_ascii(string) {
127 FieldText(FieldTextInner::Borrowed(string))
128 } else {
129 panic!("non-ASCII string passed to `FieldText::assert()`");
130 }
131 }
132}
133
134fn is_printable_ascii(string: &str) -> bool {
135 string.chars().all(char_is_printable_ascii)
137}
138
139fn char_is_printable_ascii(c: char) -> bool {
140 c.is_ascii() && !c.is_ascii_control()
141}
142
143impl AsRef<str> for FieldText<'_> {
144 fn as_ref(&self) -> &str {
145 &self.0
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151#[repr(transparent)]
152pub struct InvalidText<T>(T);
153
154impl<T> InvalidText<T> {
155 pub fn inner(&self) -> &T {
157 &self.0
158 }
159
160 pub fn into_inner(self) -> T {
162 self.0
163 }
164}
165
166impl fmt::Display for InvalidText<&str> {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 write!(f, "string was not printable ASCII: {}", self.0)
169 }
170}
171
172impl fmt::Display for InvalidText<&[u8]> {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 write!(f, "bytes were not printable ASCII: {:?}", self.0)
175 }
176}
177
178#[cfg(feature = "std")]
179impl fmt::Display for InvalidText<std::string::String> {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 write!(f, "string was not printable ASCII: {}", self.0)
182 }
183}
184
185impl<'string> TryFrom<&'string str> for FieldText<'string> {
186 type Error = InvalidText<&'string str>;
187
188 fn try_from(value: &'string str) -> Result<Self, Self::Error> {
189 if is_printable_ascii(value) {
190 Ok(Self(FieldTextInner::Borrowed(value)))
191 } else {
192 Err(InvalidText(value))
193 }
194 }
195}
196
197#[cfg(feature = "std")]
199impl std::str::FromStr for FieldText<'static> {
200 type Err = InvalidText<std::string::String>;
201
202 fn from_str(s: &str) -> Result<Self, Self::Err> {
203 use std::borrow::ToOwned;
204 s.to_owned().try_into()
205 }
206}
207
208impl<'bytes> TryFrom<&'bytes [u8]> for FieldText<'bytes> {
209 type Error = InvalidText<&'bytes [u8]>;
210
211 fn try_from(value: &'bytes [u8]) -> Result<Self, Self::Error> {
212 if let Ok(value_str) = core::str::from_utf8(value) {
213 value_str.try_into().map_err(|_| InvalidText(value))
215 } else {
216 Err(InvalidText(value))
217 }
218 }
219}
220
221#[cfg(feature = "std")]
222impl TryFrom<std::string::String> for FieldText<'_> {
223 type Error = InvalidText<std::string::String>;
224
225 fn try_from(value: std::string::String) -> Result<Self, Self::Error> {
226 if is_printable_ascii(&value) {
227 Ok(Self(FieldTextInner::Owned(value)))
228 } else {
229 Err(InvalidText(value))
230 }
231 }
232}
233
234impl PartialEq<&str> for FieldText<'_> {
235 fn eq(&self, other: &&str) -> bool {
236 self.0 == *other
237 }
238}
239
240impl PartialEq<FieldText<'_>> for &str {
241 fn eq(&self, other: &FieldText<'_>) -> bool {
242 *self == other.0
243 }
244}
245
246impl fmt::Display for FieldText<'_> {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 <_ as fmt::Display>::fmt(&self.0, f)
249 }
250}