Files
tuliprox/shared/src/utils/string_utils.rs
T

161 lines
3.7 KiB
Rust
Raw Normal View History

2025-07-08 18:23:03 +02:00
use std::borrow::Cow;
2024-11-04 18:46:56 +01:00
pub trait Capitalize {
2024-09-18 19:50:38 +02:00
fn capitalize(&self) -> String;
}
// Implement the Capitalize trait for &str
2025-05-26 10:43:03 +02:00
impl<T: AsRef<str>> Capitalize for T {
2024-09-18 19:50:38 +02:00
fn capitalize(&self) -> String {
2025-05-26 10:43:03 +02:00
let s = self.as_ref();
let mut chars = s.chars();
let first = chars
.next()
2025-05-13 21:50:08 +02:00
.map(|c| c.to_uppercase().collect::<String>())
.unwrap_or_default();
let rest = chars.as_str().to_lowercase();
first + &rest
2024-09-18 19:50:38 +02:00
}
}
2025-02-14 17:51:33 +01:00
pub fn get_trimmed_string(value: &Option<String>) -> Option<String> {
if let Some(v) = value {
let trimmed = v.trim();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
None
}
2025-04-28 19:52:59 +02:00
pub fn generate_random_string(length: usize) -> String {
2025-08-28 00:37:20 +02:00
let mut rng = fastrand::Rng::new();
2025-08-28 00:13:02 +02:00
let random_string: String = (0..length).map(|_| rng.alphanumeric()).collect();
2025-04-28 19:52:59 +02:00
random_string
}
// compare 2 small vecs without HashSet
pub fn small_vecs_equal_unordered<T: PartialEq>(a: &[T], b: &[T]) -> bool {
if a.len() != b.len() {
return false;
}
for item in a {
if !b.iter().any(|x| x == item) {
return false;
}
}
true
}
pub fn get_non_empty_str<'a>(first: &'a str, second: &'a str, third: &'a str) -> &'a str {
if !first.is_empty() {
first
} else if !second.is_empty() {
second
} else {
third
}
}
2025-09-01 13:33:34 +02:00
pub fn is_blank_optional_string(s: &Option<String>) -> bool {
s.is_none() || s.as_ref().is_some_and(|s| s.trim().is_empty())
}
2025-07-24 14:27:57 +02:00
pub fn trim_slash(s: &str) -> Cow<'_, str> {
2025-07-08 18:23:03 +02:00
let trimmed = s.trim_matches('/');
if trimmed.len() == s.len() {
Cow::Borrowed(s) // Keine Änderung → kein Clone
} else {
Cow::Owned(trimmed.to_string()) // Änderung → neue String
}
}
2025-07-24 14:27:57 +02:00
pub fn trim_last_slash(s: &str) -> Cow<'_, str> {
2025-07-08 18:23:03 +02:00
if s.ends_with('/') {
2025-07-11 08:58:04 +02:00
if let Some(stripped) = s.strip_suffix('/') {
return Cow::Owned(stripped.to_string())
}
2025-07-08 18:23:03 +02:00
}
2025-07-11 08:58:04 +02:00
Cow::Borrowed(s)
2025-07-08 18:23:03 +02:00
}
2025-07-17 17:20:59 +02:00
pub trait Substring {
fn substring(&self, from: usize, to: usize) -> String;
}
impl Substring for String {
fn substring(&self, from: usize, to: usize) -> String {
self.chars().skip(from).take(to - from).collect()
}
}
pub fn truncate_string(s: &str, max_len: usize) -> String {
if s.chars().count() <= max_len {
s.to_string()
} else {
s.chars().take(max_len).collect()
}
}
2025-10-15 20:32:50 +02:00
pub fn mask_credentials(s: &str) -> String {
match s.chars().next() {
Some(first) => format!("{}...", first),
None => "...".to_string(),
}
}
pub fn humanize_snake_case(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let mut capitalize_next = true;
for c in s.chars() {
if c == '_' {
result.push(' ');
capitalize_next = true;
} else if capitalize_next {
for up in c.to_uppercase() {
result.push(up);
}
capitalize_next = false;
} else {
result.push(c);
}
}
result
}
2025-10-25 20:40:58 +02:00
pub trait StringExt {
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str;
}
impl StringExt for String {
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
}
2025-04-28 19:52:59 +02:00
#[cfg(test)]
mod test {
use std::collections::HashSet;
2025-06-24 01:06:25 +02:00
use crate::utils::Capitalize;
use super::generate_random_string;
2025-04-28 19:52:59 +02:00
#[test]
fn test_generate_random_string() {
let mut strings = HashSet::new();
for _i in 0..100 {
strings.insert(generate_random_string(5));
}
assert_eq!(strings.len(), 100);
}
#[test]
fn test_capitalize() {
assert_eq!("hELLO".capitalize(), "Hello");
}
}