Skip to content

Commit

Permalink
Add StrExt, to_lowercase_smolstr & friends
Browse files Browse the repository at this point in the history
  • Loading branch information
alexheretic authored and Veykril committed Feb 8, 2024
1 parent 0fb3a13 commit fe7064e
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 1 deletion.
55 changes: 55 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,61 @@ pub trait ToSmolStr {
fn to_smolstr(&self) -> SmolStr;
}

/// [`str`] methods producing [`SmolStr`]s.
pub trait StrExt: private::Sealed {
/// Returns the lowercase equivalent of this string slice as a new [`SmolStr`],
/// potentially without allocating.
///
/// See [`str::to_lowercase`].
fn to_lowercase_smolstr(&self) -> SmolStr;

/// Returns the uppercase equivalent of this string slice as a new [`SmolStr`],
/// potentially without allocating.
///
/// See [`str::to_uppercase`].
fn to_uppercase_smolstr(&self) -> SmolStr;

/// Returns the ASCII lowercase equivalent of this string slice as a new [`SmolStr`],
/// potentially without allocating.
///
/// See [`str::to_ascii_lowercase`].
fn to_ascii_lowercase_smolstr(&self) -> SmolStr;

/// Returns the ASCII uppercase equivalent of this string slice as a new [`SmolStr`],
/// potentially without allocating.
///
/// See [`str::to_ascii_uppercase`].
fn to_ascii_uppercase_smolstr(&self) -> SmolStr;
}

impl StrExt for str {
#[inline]
fn to_lowercase_smolstr(&self) -> SmolStr {
SmolStr::from_char_iter(self.chars().flat_map(|c| c.to_lowercase()))
}

#[inline]
fn to_uppercase_smolstr(&self) -> SmolStr {
SmolStr::from_char_iter(self.chars().flat_map(|c| c.to_uppercase()))
}

#[inline]
fn to_ascii_lowercase_smolstr(&self) -> SmolStr {
SmolStr::from_char_iter(self.chars().map(|c| c.to_ascii_lowercase()))
}

#[inline]
fn to_ascii_uppercase_smolstr(&self) -> SmolStr {
SmolStr::from_char_iter(self.chars().map(|c| c.to_ascii_uppercase()))
}
}

mod private {
/// No downstream impls allowed.
pub trait Sealed {}
impl Sealed for str {}
}

/// Formats arguments to a [`SmolStr`], potentially without allocating.
///
/// See [`alloc::format!`] or [`format_args!`] for syntax documentation.
Expand Down
45 changes: 44 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ fn test_from_char_iterator() {
// String which has too many characters to even consider inlining: Chars::size_hint uses
// (`len` + 3) / 4. With `len` = 89, this results in 23, so `from_iter` will immediately
// heap allocate
let raw: String = std::iter::repeat('a').take(23 * 4 + 1).collect();
let raw = "a".repeat(23 * 4 + 1);
let s: SmolStr = raw.chars().collect();
assert_eq!(s.as_str(), raw);
assert!(s.is_heap_allocated());
Expand Down Expand Up @@ -270,3 +270,46 @@ fn test_to_smolstr() {
assert_eq!(a, smol_str::format_smolstr!("{}", a));
}
}

#[cfg(test)]
mod test_str_ext {
use smol_str::StrExt;

#[test]
fn large() {
let lowercase = "aaaaaaAAAAAaaaaaaaaaaaaaaaaaaaaaAAAAaaaaaaaaaaaaaa".to_lowercase_smolstr();
assert_eq!(
lowercase,
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
);
assert!(lowercase.is_heap_allocated());
}

#[test]
fn to_lowercase() {
let lowercase = "aßΔC".to_lowercase_smolstr();
assert_eq!(lowercase, "aßδc");
assert!(!lowercase.is_heap_allocated());
}

#[test]
fn to_uppercase() {
let uppercase = "aßΔC".to_uppercase_smolstr();
assert_eq!(uppercase, "ASSΔC");
assert!(!uppercase.is_heap_allocated());
}

#[test]
fn to_ascii_lowercase() {
let uppercase = "aßΔC".to_ascii_lowercase_smolstr();
assert_eq!(uppercase, "aßΔc");
assert!(!uppercase.is_heap_allocated());
}

#[test]
fn to_ascii_uppercase() {
let uppercase = "aßΔC".to_ascii_uppercase_smolstr();
assert_eq!(uppercase, "AßΔC");
assert!(!uppercase.is_heap_allocated());
}
}

0 comments on commit fe7064e

Please sign in to comment.