1#![no_std]
6
7extern crate alloc;
8
9use alloc::{
10 boxed::Box,
11 string::{String, ToString},
12 vec::Vec,
13};
14use core::{borrow::Borrow, fmt, hash, ops, str};
15
16use bytes::Bytes;
17
18#[derive(Clone, Default, Eq, PartialOrd, Ord)]
20pub struct ByteString(Bytes);
21
22impl ByteString {
23 pub const fn new() -> Self {
25 ByteString(Bytes::new())
26 }
27
28 pub fn as_bytes(&self) -> &Bytes {
30 &self.0
31 }
32
33 pub fn into_bytes(self) -> Bytes {
35 self.0
36 }
37
38 pub const fn from_static(src: &'static str) -> ByteString {
40 Self(Bytes::from_static(src.as_bytes()))
41 }
42
43 pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString {
51 Self(src)
52 }
53
54 pub fn split_at(&self, mid: usize) -> (ByteString, ByteString) {
61 let this: &str = self.as_ref();
62 let _valid_midpoint_check = this.split_at(mid);
63
64 let mut bytes = self.0.clone();
65 let first = bytes.split_to(mid);
66 let last = bytes;
67
68 unsafe {
69 (
70 ByteString::from_bytes_unchecked(first),
71 ByteString::from_bytes_unchecked(last),
72 )
73 }
74 }
75
76 pub fn slice_ref(&self, subset: &str) -> Self {
111 Self(self.0.slice_ref(subset.as_bytes()))
112 }
113}
114
115impl PartialEq<str> for ByteString {
116 fn eq(&self, other: &str) -> bool {
117 &self[..] == other
118 }
119}
120
121impl<T: AsRef<str>> PartialEq<T> for ByteString {
122 fn eq(&self, other: &T) -> bool {
123 &self[..] == other.as_ref()
124 }
125}
126
127impl AsRef<ByteString> for ByteString {
128 fn as_ref(&self) -> &ByteString {
129 self
130 }
131}
132
133impl AsRef<[u8]> for ByteString {
134 fn as_ref(&self) -> &[u8] {
135 self.0.as_ref()
136 }
137}
138
139impl AsRef<str> for ByteString {
140 fn as_ref(&self) -> &str {
141 self
142 }
143}
144
145impl hash::Hash for ByteString {
146 fn hash<H: hash::Hasher>(&self, state: &mut H) {
147 (**self).hash(state);
148 }
149}
150
151impl ops::Deref for ByteString {
152 type Target = str;
153
154 #[inline]
155 fn deref(&self) -> &str {
156 let bytes = self.0.as_ref();
157 unsafe { str::from_utf8_unchecked(bytes) }
159 }
160}
161
162impl Borrow<str> for ByteString {
163 fn borrow(&self) -> &str {
164 self
165 }
166}
167
168impl From<String> for ByteString {
169 #[inline]
170 fn from(value: String) -> Self {
171 Self(Bytes::from(value))
172 }
173}
174
175impl From<&str> for ByteString {
176 #[inline]
177 fn from(value: &str) -> Self {
178 Self(Bytes::copy_from_slice(value.as_ref()))
179 }
180}
181
182impl From<Box<str>> for ByteString {
183 #[inline]
184 fn from(value: Box<str>) -> Self {
185 Self(Bytes::from(value.into_boxed_bytes()))
186 }
187}
188
189impl From<ByteString> for String {
190 #[inline]
191 fn from(value: ByteString) -> Self {
192 value.to_string()
193 }
194}
195
196impl TryFrom<&[u8]> for ByteString {
197 type Error = str::Utf8Error;
198
199 #[inline]
200 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
201 let _ = str::from_utf8(value)?;
202 Ok(ByteString(Bytes::copy_from_slice(value)))
203 }
204}
205
206impl TryFrom<Vec<u8>> for ByteString {
207 type Error = str::Utf8Error;
208
209 #[inline]
210 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
211 let buf = String::from_utf8(value).map_err(|err| err.utf8_error())?;
212 Ok(ByteString(Bytes::from(buf)))
213 }
214}
215
216impl TryFrom<Bytes> for ByteString {
217 type Error = str::Utf8Error;
218
219 #[inline]
220 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
221 let _ = str::from_utf8(value.as_ref())?;
222 Ok(ByteString(value))
223 }
224}
225
226impl TryFrom<bytes::BytesMut> for ByteString {
227 type Error = str::Utf8Error;
228
229 #[inline]
230 fn try_from(value: bytes::BytesMut) -> Result<Self, Self::Error> {
231 let _ = str::from_utf8(&value)?;
232 Ok(ByteString(value.freeze()))
233 }
234}
235
236macro_rules! array_impls {
237 ($($len:expr)+) => {
238 $(
239 impl TryFrom<[u8; $len]> for ByteString {
240 type Error = str::Utf8Error;
241
242 #[inline]
243 fn try_from(value: [u8; $len]) -> Result<Self, Self::Error> {
244 ByteString::try_from(&value[..])
245 }
246 }
247
248 impl TryFrom<&[u8; $len]> for ByteString {
249 type Error = str::Utf8Error;
250
251 #[inline]
252 fn try_from(value: &[u8; $len]) -> Result<Self, Self::Error> {
253 ByteString::try_from(&value[..])
254 }
255 }
256 )+
257 }
258}
259
260array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32);
261
262impl fmt::Debug for ByteString {
263 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
264 (**self).fmt(fmt)
265 }
266}
267
268impl fmt::Display for ByteString {
269 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
270 (**self).fmt(fmt)
271 }
272}
273
274#[cfg(feature = "serde")]
275mod serde {
276 use alloc::string::String;
277
278 use serde_core::{
279 de::{Deserialize, Deserializer},
280 ser::{Serialize, Serializer},
281 };
282
283 use super::ByteString;
284
285 impl Serialize for ByteString {
286 #[inline]
287 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
288 where
289 S: Serializer,
290 {
291 serializer.serialize_str(self.as_ref())
292 }
293 }
294
295 impl<'de> Deserialize<'de> for ByteString {
296 #[inline]
297 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
298 where
299 D: Deserializer<'de>,
300 {
301 String::deserialize(deserializer).map(ByteString::from)
302 }
303 }
304
305 #[cfg(test)]
306 mod serde_impl_tests {
307 use serde_core::de::DeserializeOwned;
308 use static_assertions::assert_impl_all;
309
310 use super::*;
311
312 assert_impl_all!(ByteString: Serialize, DeserializeOwned);
313 }
314}
315
316#[cfg(test)]
317mod test {
318 use alloc::{borrow::ToOwned, format, vec};
319 use core::{
320 hash::{Hash, Hasher},
321 panic::{RefUnwindSafe, UnwindSafe},
322 };
323
324 use ahash::AHasher;
325 use static_assertions::assert_impl_all;
326
327 use super::*;
328
329 assert_impl_all!(ByteString: Send, Sync, Unpin, Sized);
330 assert_impl_all!(ByteString: Clone, Default, Eq, PartialOrd, Ord);
331 assert_impl_all!(ByteString: fmt::Debug, fmt::Display);
332 assert_impl_all!(ByteString: UnwindSafe, RefUnwindSafe);
333
334 #[test]
335 fn eq() {
336 let s: ByteString = ByteString::from_static("test");
337 assert_eq!(s, "test");
338 assert_eq!(s, *"test");
339 assert_eq!(s, "test".to_owned());
340 }
341
342 #[test]
343 fn new() {
344 let _: ByteString = ByteString::new();
345 }
346
347 #[test]
348 fn as_bytes() {
349 let buf = ByteString::new();
350 assert!(buf.as_bytes().is_empty());
351
352 let buf = ByteString::from("hello");
353 assert_eq!(buf.as_bytes(), "hello");
354 }
355
356 #[test]
357 fn from_bytes_unchecked() {
358 let buf = unsafe { ByteString::from_bytes_unchecked(Bytes::new()) };
359 assert!(buf.is_empty());
360
361 let buf = unsafe { ByteString::from_bytes_unchecked(Bytes::from("hello")) };
362 assert_eq!(buf, "hello");
363 }
364
365 #[test]
366 fn as_ref() {
367 let buf = ByteString::new();
368
369 let _: &ByteString = buf.as_ref();
370 let _: &[u8] = buf.as_ref();
371 }
372
373 #[test]
374 fn borrow() {
375 let buf = ByteString::new();
376
377 let _: &str = buf.borrow();
378 }
379
380 #[test]
381 fn hash() {
382 let mut hasher1 = AHasher::default();
383 "str".hash(&mut hasher1);
384
385 let mut hasher2 = AHasher::default();
386 let s = ByteString::from_static("str");
387 s.hash(&mut hasher2);
388 assert_eq!(hasher1.finish(), hasher2.finish());
389 }
390
391 #[test]
392 fn from_string() {
393 let s: ByteString = "hello".to_owned().into();
394 assert_eq!(&s, "hello");
395 let t: &str = s.as_ref();
396 assert_eq!(t, "hello");
397 }
398
399 #[test]
400 fn from_str() {
401 let _: ByteString = "str".into();
402 let _: ByteString = "str".to_owned().into_boxed_str().into();
403 }
404
405 #[test]
406 fn to_string() {
407 let buf = ByteString::from("foo");
408 assert_eq!(String::from(buf), "foo");
409 }
410
411 #[test]
412 fn from_static_str() {
413 static _S: ByteString = ByteString::from_static("hello");
414 let _ = ByteString::from_static("str");
415 }
416
417 #[test]
418 fn try_from_slice() {
419 let _ = ByteString::try_from(b"nice bytes").unwrap();
420 }
421
422 #[test]
423 fn try_from_array() {
424 assert_eq!(
425 ByteString::try_from([b'h', b'i']).unwrap(),
426 ByteString::from_static("hi")
427 );
428 }
429
430 #[test]
431 fn try_from_vec() {
432 let _ = ByteString::try_from(vec![b'f', b'o', b'o']).unwrap();
433 ByteString::try_from(vec![0, 159, 146, 150]).unwrap_err();
434 }
435
436 #[test]
437 fn try_from_bytes() {
438 let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap();
439 }
440
441 #[test]
442 fn try_from_bytes_mut() {
443 let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap();
444 }
445
446 #[test]
447 fn display() {
448 let buf = ByteString::from("bar");
449 assert_eq!(format!("{buf}"), "bar");
450 }
451
452 #[test]
453 fn debug() {
454 let buf = ByteString::from("baz");
455 assert_eq!(format!("{buf:?}"), r#""baz""#);
456 }
457
458 #[cfg(feature = "serde")]
459 #[test]
460 fn serialize() {
461 let s: ByteString = serde_json::from_str(r#""nice bytes""#).unwrap();
462 assert_eq!(s, "nice bytes");
463 }
464
465 #[cfg(feature = "serde")]
466 #[test]
467 fn deserialize() {
468 let s = serde_json::to_string(&ByteString::from_static("nice bytes")).unwrap();
469 assert_eq!(s, r#""nice bytes""#);
470 }
471
472 #[test]
473 fn slice_ref() {
474 let string = ByteString::from_static(" foo ");
475 let subset = string.trim();
476 let substring = string.slice_ref(subset);
478 assert_eq!(substring, "foo");
479 }
480
481 #[test]
482 #[should_panic]
483 fn slice_ref_catches_not_a_subset() {
484 ByteString::from_static("foo bar").slice_ref("foo");
487 }
488
489 #[test]
490 fn split_at() {
491 let buf = ByteString::from_static("foo bar");
492
493 let (first, last) = buf.split_at(0);
494 assert_eq!(ByteString::from_static(""), first);
495 assert_eq!(ByteString::from_static("foo bar"), last);
496
497 let (first, last) = buf.split_at(4);
498 assert_eq!(ByteString::from_static("foo "), first);
499 assert_eq!(ByteString::from_static("bar"), last);
500
501 let (first, last) = buf.split_at(7);
502 assert_eq!(ByteString::from_static("foo bar"), first);
503 assert_eq!(ByteString::from_static(""), last);
504 }
505
506 #[test]
507 #[should_panic = "byte index 1 is not a char boundary;"]
508 fn split_at_invalid_code_point() {
509 ByteString::from_static("ยต").split_at(1);
510 }
511
512 #[test]
513 #[should_panic = "byte index 9 is out of bounds"]
514 fn split_at_outside_string() {
515 ByteString::from_static("foo").split_at(9);
516 }
517}