use regex::Regex;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct TextHandler {
value: String,
}
impl TextHandler {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.value
}
#[must_use]
pub fn into_string(self) -> String {
self.value
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.value.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.value.len()
}
#[must_use]
pub fn strip(&self) -> TextHandler {
TextHandler::new(self.value.trim())
}
#[must_use]
pub fn to_lowercase(&self) -> TextHandler {
TextHandler::new(self.value.to_lowercase())
}
#[must_use]
pub fn to_uppercase(&self) -> TextHandler {
TextHandler::new(self.value.to_uppercase())
}
#[must_use]
pub fn contains_str(&self, pattern: &str) -> bool {
self.value.contains(pattern)
}
#[must_use]
pub fn replace_str(&self, from: &str, to: &str) -> TextHandler {
TextHandler::new(self.value.replace(from, to))
}
#[must_use]
pub fn starts_with_str(&self, prefix: &str) -> bool {
self.value.starts_with(prefix)
}
#[must_use]
pub fn ends_with_str(&self, suffix: &str) -> bool {
self.value.ends_with(suffix)
}
#[must_use]
pub fn split_str(&self, delimiter: &str) -> Vec<TextHandler> {
self.value.split(delimiter).map(TextHandler::new).collect()
}
#[must_use]
pub fn clean(&self, remove_entities: bool) -> TextHandler {
let mut s = String::with_capacity(self.value.len());
let mut last_was_space = false;
for c in self.value.chars() {
match c {
'\r' => {} '\t' | '\n' | ' ' => {
if !last_was_space {
s.push(' ');
}
last_was_space = true;
}
c => {
s.push(c);
last_was_space = false;
}
}
}
let mut s = s.trim().to_string();
if remove_entities {
s = decode_html_entities(&s);
}
TextHandler::new(s)
}
pub fn json(&self) -> Result<serde_json::Value, serde_json::Error> {
serde_json::from_str(&self.value)
}
#[must_use]
pub fn re(
&self,
pattern: &str,
replace_entities: bool,
clean_match: bool,
case_sensitive: bool,
) -> Vec<TextHandler> {
let text = if replace_entities {
decode_html_entities(&self.value)
} else {
self.value.clone()
};
let full_pattern = if case_sensitive {
pattern.to_string()
} else {
format!("(?i){}", pattern)
};
let re = match Regex::new(&full_pattern) {
Ok(r) => r,
Err(_) => return vec![],
};
let mut results = Vec::new();
for caps in re.captures_iter(&text) {
let matched = if caps.len() > 1 {
caps.get(1).map(|m| m.as_str())
} else {
caps.get(0).map(|m| m.as_str())
};
if let Some(s) = matched {
let val = if clean_match { s.trim() } else { s };
results.push(TextHandler::new(val));
}
}
results
}
#[must_use]
pub fn re_first(
&self,
pattern: &str,
replace_entities: bool,
clean_match: bool,
case_sensitive: bool,
) -> Option<TextHandler> {
self.re(pattern, replace_entities, clean_match, case_sensitive)
.into_iter()
.next()
}
#[must_use]
pub fn get(&self) -> &TextHandler {
self
}
#[must_use]
pub fn getall(&self) -> Vec<TextHandler> {
vec![self.clone()]
}
}
impl fmt::Display for TextHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl AsRef<str> for TextHandler {
fn as_ref(&self) -> &str {
&self.value
}
}
impl From<String> for TextHandler {
fn from(s: String) -> Self {
TextHandler::new(s)
}
}
impl From<&str> for TextHandler {
fn from(s: &str) -> Self {
TextHandler::new(s)
}
}
fn decode_html_entities(s: &str) -> String {
s.replace("<", "<")
.replace(">", ">")
.replace(""", "\"")
.replace("'", "'")
.replace("'", "'")
.replace(" ", " ")
.replace("&", "&")
}