#[cfg(feature = "cstring")]
use std::ffi::CString;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
pub trait StringBehavior {
fn as_bytes(&self) -> &[u8];
fn as_str(&self) -> &str;
#[cfg(feature = "cstring")]
fn extract(&self) -> CString;
#[cfg(not(feature = "cstring"))]
fn extract(&self) -> String;
fn as_string(&self) -> String;
fn to_uppercase(&self) -> Self;
fn to_lowercase(&self) -> Self;
fn trim(&self) -> Self;
fn replace(&self, from: &str, to: &str) -> Self;
fn concat<T: AsRef<str>>(&self, other: T) -> Self;
fn from_utf8(value: Vec<u8>) -> Self;
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, PartialOrd, Ord)]
pub struct StringB {
#[cfg(feature = "cstring")]
pub value: CString,
#[cfg(not(feature = "cstring"))]
pub value: String,
}
impl StringB {
pub fn len(&self) -> usize {
self.as_bytes().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[cfg(feature = "cstring")]
pub fn as_string(&self) -> String {
self.value.to_str().unwrap().to_string()
}
#[cfg(not(feature = "cstring"))]
pub fn as_string(&self) -> String {
self.value.clone()
}
}
impl StringBehavior for StringB {
fn as_bytes(&self) -> &[u8] {
self.value.as_bytes()
}
#[cfg(feature = "cstring")]
fn as_str(&self) -> &str {
self.value.to_str().expect("CString is not valid UTF-8")
}
#[cfg(not(feature = "cstring"))]
fn as_str(&self) -> &str {
self.value.as_str()
}
#[cfg(feature = "cstring")]
fn as_string(&self) -> String {
self.value
.to_str()
.expect("CString is not valid UTF-8")
.to_string()
}
#[cfg(not(feature = "cstring"))]
fn as_string(&self) -> String {
self.value.clone()
}
#[cfg(feature = "cstring")]
fn extract(&self) -> CString {
self.value.clone()
}
#[cfg(not(feature = "cstring"))]
fn extract(&self) -> String {
self.as_string()
}
fn to_uppercase(&self) -> Self {
let upper_str = self.as_str().to_uppercase();
StringB::from(upper_str)
}
fn to_lowercase(&self) -> Self {
let lower_str = self.as_str().to_lowercase();
StringB::from(lower_str)
}
fn trim(&self) -> Self {
let trimmed_str = self.as_str().trim();
StringB::from(trimmed_str)
}
fn replace(&self, from: &str, to: &str) -> Self {
let replaced_str = self.as_str().replace(from, to);
StringB::from(replaced_str)
}
fn concat<T: AsRef<str>>(&self, other: T) -> Self {
let mut result = String::from(self.as_str());
result.push_str(other.as_ref());
StringB::from(result)
}
#[cfg(not(feature = "cstring"))]
fn from_utf8(value: Vec<u8>) -> Self {
StringB::from(String::from_utf8(value).unwrap())
}
#[cfg(feature = "cstring")]
fn from_utf8(value: Vec<u8>) -> Self {
StringB {
value: CString::new(value).unwrap(),
}
}
}
#[cfg(feature = "cstring")]
impl Display for StringB {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_string())
}
}
#[cfg(not(feature = "cstring"))]
impl Display for StringB {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_string())
}
}
impl Deref for StringB {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
#[cfg(not(feature = "cstring"))]
impl From<String> for StringB {
fn from(value: String) -> Self {
StringB {
value: value.clone(),
}
}
}
#[cfg(not(feature = "cstring"))]
impl From<&str> for StringB {
fn from(value: &str) -> Self {
StringB {
value: value.to_string(),
}
}
}
#[cfg(feature = "cstring")]
impl From<String> for StringB {
fn from(value: String) -> Self {
StringB {
value: CString::new(value).unwrap(),
}
}
}
#[cfg(feature = "cstring")]
impl From<&str> for StringB {
fn from(value: &str) -> Self {
StringB {
value: CString::new(value).unwrap(),
}
}
}
#[cfg(feature = "cstring")]
impl From<CString> for StringB {
fn from(value: CString) -> Self {
StringB {
value: value.clone(),
}
}
}
impl From<&Vec<u8>> for StringB {
fn from(value: &Vec<u8>) -> Self {
StringB::from_utf8(value.clone())
}
}
impl From<Vec<u8>> for StringB {
fn from(value: Vec<u8>) -> Self {
StringB::from_utf8(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_len() {
let s = StringB::from("Hello");
assert_eq!(s.len(), 5);
}
#[test]
fn test_is_empty() {
let s = StringB::from("");
assert!(s.is_empty());
}
#[test]
fn test_to_uppercase() {
let s = StringB::from("hello");
assert_eq!(s.to_uppercase().as_str(), "HELLO");
}
#[test]
fn test_to_lowercase() {
let s = StringB::from("HELLO");
assert_eq!(s.to_lowercase().as_str(), "hello");
}
#[test]
fn test_trim() {
let s = StringB::from(" hello ");
assert_eq!(s.trim().as_str(), "hello");
}
#[test]
fn test_replace() {
let s = StringB::from("hello world");
assert_eq!(s.replace("world", "planet").as_str(), "hello planet");
}
#[test]
fn test_concat() {
let s1 = StringB::from("hello");
let s2 = " world";
assert_eq!(s1.concat(s2).as_str(), "hello world");
}
}