Skip to content

Commit 48a9d8b

Browse files
committed
Implement TryFrom<Box<str>> for FixedString
1 parent d3c15bd commit 48a9d8b

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

src/length.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,50 @@ impl<T> std::fmt::Display for InvalidLength<T> {
4242
}
4343
}
4444

45+
#[derive(Debug)]
46+
pub struct InvalidStrLength {
47+
pub(crate) backtrace: std::backtrace::Backtrace,
48+
type_name: &'static str,
49+
original: Box<str>,
50+
}
51+
52+
impl InvalidStrLength {
53+
/// Returns the original [`Box<str>`] that could not be converted from.
54+
pub fn get_inner(self) -> Box<str> {
55+
self.original
56+
}
57+
}
58+
59+
impl std::fmt::Display for InvalidStrLength {
60+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61+
write!(
62+
f,
63+
"Cannot fit {} into {}:\n\n{}",
64+
self.original.len(),
65+
self.type_name,
66+
self.backtrace
67+
)
68+
}
69+
}
70+
71+
impl TryFrom<InvalidLength<u8>> for InvalidStrLength {
72+
type Error = std::str::Utf8Error;
73+
74+
fn try_from(value: InvalidLength<u8>) -> Result<Self, Self::Error> {
75+
let original = if let Err(err) = std::str::from_utf8(&value.original) {
76+
return Err(err);
77+
} else {
78+
unsafe { std::str::from_boxed_utf8_unchecked(value.original) }
79+
};
80+
81+
Ok(Self {
82+
original,
83+
type_name: value.type_name,
84+
backtrace: value.backtrace,
85+
})
86+
}
87+
}
88+
4589
pub(crate) const fn get_heap_threshold<LenT>() -> usize {
4690
std::mem::size_of::<usize>() + std::mem::size_of::<LenT>() - 1
4791
}

src/string.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{cmp::PartialEq, fmt::Write as _, hash::Hash};
33
use crate::{
44
array::FixedArray,
55
inline::InlineString,
6-
length::{get_heap_threshold, SmallLen, ValidLength},
6+
length::{get_heap_threshold, InvalidStrLength, SmallLen, ValidLength},
77
};
88

99
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
@@ -165,6 +165,24 @@ impl<LenT: ValidLength> std::fmt::Debug for FixedString<LenT> {
165165
}
166166
}
167167

168+
impl<LenT: ValidLength> TryFrom<Box<str>> for FixedString<LenT> {
169+
type Error = InvalidStrLength;
170+
171+
fn try_from(value: Box<str>) -> Result<Self, Self::Error> {
172+
if value.len() <= get_heap_threshold::<LenT>() {
173+
let inner = InlineString::from_str(&value);
174+
return Ok(Self(FixedStringRepr::Inline(inner)));
175+
}
176+
177+
match value.into_boxed_bytes().try_into() {
178+
Ok(val) => Ok(Self(FixedStringRepr::Heap(val))),
179+
Err(err) => Err(err
180+
.try_into()
181+
.expect("Box<str> -> Box<[u8]> should stay valid UTF8")),
182+
}
183+
}
184+
}
185+
168186
#[cfg(any(feature = "log_using_log", feature = "log_using_tracing"))]
169187
impl<LenT: ValidLength> From<String> for FixedString<LenT> {
170188
fn from(value: String) -> Self {
@@ -228,15 +246,15 @@ impl<LenT: ValidLength> serde::Serialize for FixedString<LenT> {
228246
}
229247
}
230248

231-
#[cfg(all(test, any(feature = "log_using_log", feature = "log_using_tracing")))]
249+
#[cfg(test)]
232250
mod test {
233251
use super::*;
234252

235253
#[test]
236254
fn check_u8_roundtrip() {
237255
for i in 0..=u8::MAX {
238-
let original = "a".repeat(i.into());
239-
let fixed = FixedString::<u8>::from(original);
256+
let original = "a".repeat(i.into()).into_boxed_str();
257+
let fixed = FixedString::<u8>::try_from(original).unwrap();
240258

241259
assert!(fixed.bytes().all(|c| c == b'a'));
242260
assert_eq!(fixed.len(), i.into());

0 commit comments

Comments
 (0)