use crate::tina::constant::Constants;
use std::borrow::Cow;
use std::ffi::OsStr;
use std::fmt::{Display, Formatter};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Range, Sub, SubAssign};
use std::path::Path;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CharIndex(usize);
impl CharIndex {
pub const ZERO: CharIndex = CharIndex(0);
pub fn value(&self) -> usize {
self.0
}
pub fn value_of(v: usize) -> Self {
Self(v)
}
}
impl Display for CharIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl Add<CharIndex> for CharIndex {
type Output = CharIndex;
fn add(self, rhs: CharIndex) -> Self::Output {
CharIndex(self.0 + rhs.0)
}
}
impl AddAssign<CharIndex> for CharIndex {
fn add_assign(&mut self, rhs: CharIndex) {
self.0 += rhs.0;
}
}
impl Sub<CharIndex> for CharIndex {
type Output = CharIndex;
fn sub(self, rhs: CharIndex) -> Self::Output {
CharIndex(self.0 - rhs.0)
}
}
impl SubAssign<CharIndex> for CharIndex {
fn sub_assign(&mut self, rhs: CharIndex) {
self.0 -= rhs.0;
}
}
impl Mul<CharIndex> for CharIndex {
type Output = CharIndex;
fn mul(self, rhs: CharIndex) -> Self::Output {
CharIndex(self.0 * rhs.0)
}
}
impl MulAssign<CharIndex> for CharIndex {
fn mul_assign(&mut self, rhs: CharIndex) {
self.0 *= rhs.0
}
}
impl Div<CharIndex> for CharIndex {
type Output = CharIndex;
fn div(self, rhs: CharIndex) -> Self::Output {
CharIndex(self.0 / rhs.0)
}
}
impl DivAssign<CharIndex> for CharIndex {
fn div_assign(&mut self, rhs: CharIndex) {
self.0 /= rhs.0
}
}
pub trait OptionToString {
fn to_string(&self) -> String;
}
impl<T> OptionToString for Option<T>
where
T: Default + ToString,
{
fn to_string(&self) -> String {
match self {
None => T::default().to_string(),
Some(s) => s.to_string(),
}
}
}
pub trait AsStr {
fn as_str(&self) -> &str;
}
impl<T> AsStr for Option<T>
where
T: AsRef<str>,
{
fn as_str(&self) -> &str {
match self {
None => "",
Some(s) => s.as_ref(),
}
}
}
impl<'a, T> AsStr for &'a Option<T>
where
T: AsRef<str>,
{
fn as_str(&self) -> &str {
match Option::as_ref(self) {
None => "",
Some(s) => s.as_ref(),
}
}
}
impl<'a, T> AsStr for &'a mut Option<T>
where
T: AsRef<str>,
{
fn as_str(&self) -> &str {
match Option::as_ref(self) {
None => "",
Some(s) => s.as_ref(),
}
}
}
impl AsStr for OsStr {
fn as_str(&self) -> &str {
match self.to_str() {
None => "",
Some(s) => s,
}
}
}
impl AsStr for Path {
fn as_str(&self) -> &str {
match self.to_str() {
None => "",
Some(s) => s,
}
}
}
pub trait IntoStr<Str> {
fn into_str(self) -> Str;
}
impl<'a, T> IntoStr<&'a str> for &'a T
where
T: AsRef<str>,
{
fn into_str(self) -> &'a str {
(*self).as_ref()
}
}
impl<'a, T> IntoStr<&'a str> for &'a mut T
where
T: AsRef<str>,
{
fn into_str(self) -> &'a str {
(*self).as_ref()
}
}
impl<'a> IntoStr<&'a str> for Option<&'a str> {
fn into_str(self) -> &'a str {
match self {
None => "",
Some(v) => v,
}
}
}
impl<'a, T> IntoStr<&'a str> for Option<&'a T>
where
T: AsRef<str>,
{
fn into_str(self) -> &'a str {
match self {
None => "",
Some(v) => (*v).as_ref(),
}
}
}
impl<'a, T> IntoStr<&'a str> for Option<&'a mut T>
where
T: AsRef<str>,
{
fn into_str(self) -> &'a str {
match self {
None => "",
Some(v) => (*v).as_ref(),
}
}
}
pub trait StringExt {
fn to_md5_lowercase(&self) -> String;
fn to_md5_uppercase(&self) -> String;
fn is_http(&self) -> bool;
fn char_length(&self) -> CharIndex;
fn char_at(&self, idx: CharIndex) -> Option<char>;
fn index_char_of(&self, c: char) -> Option<CharIndex>;
fn last_index_char_of(&self, c: char) -> Option<CharIndex>;
fn index_of(&self, s: &str) -> Option<CharIndex>;
fn last_index_of(&self, s: &str) -> Option<CharIndex>;
fn substring(&self, range: Range<CharIndex>) -> Cow<str>;
fn substring_after(&self, separator: &str) -> Cow<str>;
fn substring_after_last(&self, separator: &str) -> Cow<str>;
fn substring_before(&self, separator: &str) -> Cow<str>;
fn substring_before_last(&self, separator: &str) -> Cow<str>;
fn substring_between(&self, open: &str, close: &str) -> Cow<str>;
fn substrings_between(&self, open: &str, close: &str) -> Vec<Cow<str>>;
}
#[allow(unused)]
impl<T> StringExt for T
where
T: AsRef<str>,
{
fn to_md5_lowercase(&self) -> String {
format!("{:x}", md5::compute(self.as_ref().as_bytes()))
}
fn to_md5_uppercase(&self) -> String {
format!("{:x}", md5::compute(self.as_ref().as_bytes())).to_uppercase()
}
fn is_http(&self) -> bool {
let s = self.as_ref();
s.starts_with(Constants::HTTP) || s.starts_with(Constants::HTTPS)
}
fn char_length(&self) -> CharIndex {
CharIndex(self.as_ref().chars().count())
}
fn char_at(&self, idx: CharIndex) -> Option<char> {
self.as_ref().chars().enumerate().find(|(v, _)| (*v) == idx.0).map(|v| v.1)
}
fn index_char_of(&self, c: char) -> Option<CharIndex> {
self.as_ref().chars().enumerate().find(|(_, v)| (*v) == c).map(|v| CharIndex(v.0))
}
fn last_index_char_of(&self, c: char) -> Option<CharIndex> {
let count = self.char_length();
self.as_ref().chars().rev().enumerate().find(|(_, v)| (*v) == c).map(|v| count - CharIndex(v.0) - CharIndex(1))
}
fn index_of(&self, s: &str) -> Option<CharIndex> {
if s.is_empty() {
return None;
}
let s0 = self.as_ref();
match s0.find(s) {
None => None,
Some(i0) => s0.get(0..i0).map(|s1| s1.char_length()),
}
}
fn last_index_of(&self, s: &str) -> Option<CharIndex> {
if s.is_empty() {
return None;
}
let s0 = self.as_ref();
match s0.rfind(s) {
None => None,
Some(i0) => s0.get(0..i0).map(|s1| s1.char_length()),
}
}
fn substring(&self, range: Range<CharIndex>) -> Cow<str> {
let start = range.start;
let end = range.end;
let s = self.as_ref();
let s1 = s
.chars()
.enumerate()
.filter(|(i, _)| {
let idx = CharIndex(*i);
idx >= start && idx < end
})
.map(|v| v.1)
.collect::<String>();
Cow::Owned(s1)
}
fn substring_after(&self, separator: &str) -> Cow<str> {
if separator.is_empty() {
return Cow::Borrowed("");
}
let s = self.as_ref();
match s.split_once(separator) {
None => Cow::Borrowed(""),
Some(v) => Cow::Borrowed(v.1),
}
}
fn substring_after_last(&self, separator: &str) -> Cow<str> {
if separator.is_empty() {
return Cow::Borrowed("");
}
let s = self.as_ref();
match s.rsplit_once(separator) {
None => Cow::Borrowed(""),
Some(v) => Cow::Borrowed(v.1),
}
}
fn substring_before(&self, separator: &str) -> Cow<str> {
if separator.is_empty() {
return Cow::Borrowed("");
}
let s = self.as_ref();
match s.split_once(separator) {
None => Cow::Borrowed(s),
Some(v) => Cow::Borrowed(v.0),
}
}
fn substring_before_last(&self, separator: &str) -> Cow<str> {
if separator.is_empty() {
return Cow::Borrowed("");
}
let s = self.as_ref();
match s.rsplit_once(separator) {
None => Cow::Borrowed(s),
Some(v) => Cow::Borrowed(v.0),
}
}
fn substring_between(&self, open: &str, close: &str) -> Cow<str> {
let s = self.as_ref();
if open.is_empty() && close.is_empty() {
return Cow::Borrowed(s);
}
if open.is_empty() {
return self.substring_before(close);
}
if close.is_empty() {
return self.substring_after(open);
}
let start_index = s.find(open).map(|i| i + open.len());
if let Some(start_index) = start_index {
let s1 = &s[start_index..];
let end_index = s1.find(close);
if let Some(end_index) = end_index {
let sub = &s1[..end_index];
return Cow::Borrowed(sub);
}
}
Cow::Borrowed("")
}
fn substrings_between(&self, open: &str, close: &str) -> Vec<Cow<str>> {
let mut s = self.as_ref();
if s.is_empty() || open.is_empty() || close.is_empty() {
return vec![];
}
let str_len = s.len();
let close_len = close.len();
let open_len = open.len();
let mut list = Vec::new();
if str_len <= open_len || str_len <= close_len {
return list;
}
let mut end = 0;
let mut pos = 0;
while pos < str_len - close_len {
let mut sub = &s[pos..];
let start = sub.find(open);
match start {
None => {
break;
}
Some(mut start) => {
start += open_len;
sub = &s[pos + start..];
match sub.find(close) {
None => {
break;
}
Some(v1) => {
end = pos + start + v1;
let s1 = &s[pos + start..end];
list.push(Cow::Borrowed(s1));
}
}
}
}
pos = end + close_len;
}
list
}
}
pub trait ContainsIgnoreCase<T> {
fn contains_ignore_case(&self, sub: T) -> bool;
}
pub trait ContainsIgnoreCaseOption<T> {
fn contains_ignore_case(&self, sub: T) -> bool;
}
pub trait ContainsIgnoreCaseArray<T> {
fn contains_ignore_case(&self, sub: T) -> bool;
}
impl<T: AsRef<str>, Sub: AsRef<str>> ContainsIgnoreCase<Sub> for T {
fn contains_ignore_case(&self, sub: Sub) -> bool {
let s = self.as_ref().to_lowercase();
let sub = sub.as_ref().to_lowercase();
s.contains(sub.as_str())
}
}
impl<T: AsRef<str>, Sub: AsRef<str>> ContainsIgnoreCaseOption<Sub> for Option<T> {
fn contains_ignore_case(&self, sub: Sub) -> bool {
match self {
None => false,
Some(v) => {
let s = v.as_ref().to_lowercase();
let sub = sub.as_ref().to_lowercase();
s.contains(sub.as_str())
}
}
}
}
impl<'a, T: AsRef<[&'a str]>, Sub: AsRef<str>> ContainsIgnoreCaseArray<Sub> for T {
fn contains_ignore_case(&self, sub: Sub) -> bool {
let arr = self.as_ref();
let sub = sub.as_ref().to_lowercase();
for s in arr.iter() {
if s.to_lowercase() == sub {
return true;
}
}
false
}
}
#[allow(unused)]
#[cfg(test)]
mod test {
use crate::tina::util::string::{CharIndex, StringExt};
use std::borrow::Cow;
#[test]
#[ignore]
fn test_chars() {
let s = "Löwe 老虎 Léopard Gepardi";
for (i, c) in s.char_indices() {
println!("{}: {}", i, c);
}
}
#[test]
fn test_char_length() {
let l = "Löwe 老虎 Léopard Gepardi".char_length().value();
assert_eq!(l, 23);
}
#[test]
fn test_char_at() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.char_at(CharIndex(0)), Some('L'));
assert_eq!(s.char_at(CharIndex(1)), Some('ö'));
assert_eq!(s.char_at(CharIndex(2)), Some('w'));
assert_eq!(s.char_at(CharIndex(3)), Some('e'));
assert_eq!(s.char_at(CharIndex(4)), Some(' '));
assert_eq!(s.char_at(CharIndex(5)), Some('老'));
assert_eq!(s.char_at(CharIndex(6)), Some('虎'));
assert_eq!(s.char_at(CharIndex(7)), Some(' '));
assert_eq!(s.char_at(CharIndex(8)), Some('L'));
assert_eq!(s.char_at(CharIndex(9)), Some('é'));
assert_eq!(s.char_at(CharIndex(10)), Some('o'));
assert_eq!(s.char_at(CharIndex(11)), Some('p'));
assert_eq!(s.char_at(CharIndex(12)), Some('a'));
assert_eq!(s.char_at(CharIndex(13)), Some('r'));
assert_eq!(s.char_at(CharIndex(14)), Some('d'));
assert_eq!(s.char_at(CharIndex(15)), Some(' '));
assert_eq!(s.char_at(CharIndex(16)), Some('G'));
assert_eq!(s.char_at(CharIndex(17)), Some('e'));
assert_eq!(s.char_at(CharIndex(18)), Some('p'));
assert_eq!(s.char_at(CharIndex(19)), Some('a'));
assert_eq!(s.char_at(CharIndex(20)), Some('r'));
assert_eq!(s.char_at(CharIndex(21)), Some('d'));
assert_eq!(s.char_at(CharIndex(22)), Some('i'));
assert_eq!(s.char_at(CharIndex(23)), None);
}
#[test]
fn test_index_char_of() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.index_char_of('L'), Some(CharIndex(0)));
assert_eq!(s.index_char_of('ö'), Some(CharIndex(1)));
assert_eq!(s.index_char_of('w'), Some(CharIndex(2)));
assert_eq!(s.index_char_of('e'), Some(CharIndex(3)));
assert_eq!(s.index_char_of(' '), Some(CharIndex(4)));
assert_eq!(s.index_char_of('老'), Some(CharIndex(5)));
assert_eq!(s.index_char_of('虎'), Some(CharIndex(6)));
assert_eq!(s.index_char_of(' '), Some(CharIndex(4)));
assert_eq!(s.index_char_of('L'), Some(CharIndex(0)));
assert_eq!(s.index_char_of('é'), Some(CharIndex(9)));
assert_eq!(s.index_char_of('o'), Some(CharIndex(10)));
assert_eq!(s.index_char_of('p'), Some(CharIndex(11)));
assert_eq!(s.index_char_of('a'), Some(CharIndex(12)));
assert_eq!(s.index_char_of('r'), Some(CharIndex(13)));
assert_eq!(s.index_char_of('d'), Some(CharIndex(14)));
assert_eq!(s.index_char_of(' '), Some(CharIndex(4)));
assert_eq!(s.index_char_of('G'), Some(CharIndex(16)));
assert_eq!(s.index_char_of('e'), Some(CharIndex(3)));
assert_eq!(s.index_char_of('p'), Some(CharIndex(11)));
assert_eq!(s.index_char_of('a'), Some(CharIndex(12)));
assert_eq!(s.index_char_of('r'), Some(CharIndex(13)));
assert_eq!(s.index_char_of('d'), Some(CharIndex(14)));
assert_eq!(s.index_char_of('i'), Some(CharIndex(22)));
assert_eq!(s.index_char_of('z'), None);
}
#[test]
fn test_last_index_char_of() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.last_index_char_of('L'), Some(CharIndex(8)));
assert_eq!(s.last_index_char_of('ö'), Some(CharIndex(1)));
assert_eq!(s.last_index_char_of('w'), Some(CharIndex(2)));
assert_eq!(s.last_index_char_of('e'), Some(CharIndex(17)));
assert_eq!(s.last_index_char_of(' '), Some(CharIndex(15)));
assert_eq!(s.last_index_char_of('老'), Some(CharIndex(5)));
assert_eq!(s.last_index_char_of('虎'), Some(CharIndex(6)));
assert_eq!(s.last_index_char_of(' '), Some(CharIndex(15)));
assert_eq!(s.last_index_char_of('L'), Some(CharIndex(8)));
assert_eq!(s.last_index_char_of('é'), Some(CharIndex(9)));
assert_eq!(s.last_index_char_of('o'), Some(CharIndex(10)));
assert_eq!(s.last_index_char_of('p'), Some(CharIndex(18)));
assert_eq!(s.last_index_char_of('a'), Some(CharIndex(19)));
assert_eq!(s.last_index_char_of('r'), Some(CharIndex(20)));
assert_eq!(s.last_index_char_of('d'), Some(CharIndex(21)));
assert_eq!(s.last_index_char_of(' '), Some(CharIndex(15)));
assert_eq!(s.last_index_char_of('G'), Some(CharIndex(16)));
assert_eq!(s.last_index_char_of('e'), Some(CharIndex(17)));
assert_eq!(s.last_index_char_of('p'), Some(CharIndex(18)));
assert_eq!(s.last_index_char_of('a'), Some(CharIndex(19)));
assert_eq!(s.last_index_char_of('r'), Some(CharIndex(20)));
assert_eq!(s.last_index_char_of('d'), Some(CharIndex(21)));
assert_eq!(s.last_index_char_of('i'), Some(CharIndex(22)));
assert_eq!(s.last_index_char_of('z'), None);
}
#[test]
fn test_index_of() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.index_of("Löwe 老虎 Léopard Gepardi"), Some(CharIndex(0)));
assert_eq!(s.index_of("L"), Some(CharIndex(0)));
assert_eq!(s.index_of("Löwe 老虎 L"), Some(CharIndex(0)));
assert_eq!(s.index_of("ö"), Some(CharIndex(1)));
assert_eq!(s.index_of("öwe 老虎 Léo"), Some(CharIndex(1)));
assert_eq!(s.index_of("pard"), Some(CharIndex(11)));
assert_eq!(s.index_of("abc"), None);
}
#[test]
fn test_last_index_of() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.last_index_of("Löwe 老虎 Léopard Gepardi"), Some(CharIndex(0)));
assert_eq!(s.last_index_of("L"), Some(CharIndex(8)));
assert_eq!(s.last_index_of("Löwe 老虎 L"), Some(CharIndex(0)));
assert_eq!(s.last_index_of("ö"), Some(CharIndex(1)));
assert_eq!(s.last_index_of("öwe 老虎 Léo"), Some(CharIndex(1)));
assert_eq!(s.last_index_of("pard"), Some(CharIndex(18)));
assert_eq!(s.last_index_of("abc"), None);
}
#[test]
fn test_substring() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.substring(CharIndex::ZERO..CharIndex(23)), "Löwe 老虎 Léopard Gepardi");
assert_eq!(s.substring(CharIndex::ZERO..CharIndex(24)), "Löwe 老虎 Léopard Gepardi");
assert_eq!(s.substring(CharIndex::ZERO..CharIndex(1)), "L");
assert_eq!(s.substring(CharIndex(3)..CharIndex(6)), "e 老");
assert_eq!(s.substring(CharIndex(3)..CharIndex(23)), "e 老虎 Léopard Gepardi");
assert_eq!(s.substring(CharIndex(3)..CharIndex(24)), "e 老虎 Léopard Gepardi");
assert_eq!(s.substring(CharIndex(3)..CharIndex(1)), "");
}
#[test]
fn test_substring_after() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.substring_after("L"), "öwe 老虎 Léopard Gepardi");
assert_eq!(s.substring_after("e 老"), "虎 Léopard Gepardi");
assert_eq!(s.substring_after(""), "");
assert_eq!(s.substring_after("Löwe 老虎 Léopard Gepardi"), "");
assert_eq!(s.substring_after("v"), "");
}
#[test]
fn test_substring_after_last() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.substring_after_last("L"), "éopard Gepardi");
assert_eq!(s.substring_after_last("e 老"), "虎 Léopard Gepardi");
assert_eq!(s.substring_after_last(""), "");
assert_eq!(s.substring_after_last("Löwe 老虎 Léopard Gepardi"), "");
assert_eq!(s.substring_after_last("v"), "");
}
#[test]
fn test_substring_before() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.substring_before("L"), "");
assert_eq!(s.substring_before("e 老"), "Löw");
assert_eq!(s.substring_before(""), "");
assert_eq!(s.substring_before("Löwe 老虎 Léopard Gepardi"), "");
assert_eq!(s.substring_before("v"), s);
}
#[test]
fn test_substring_before_last() {
let s = "Löwe 老虎 Léopard Gepardi";
assert_eq!(s.substring_before_last("L"), "Löwe 老虎 ");
assert_eq!(s.substring_before_last("e 老"), "Löw");
assert_eq!(s.substring_before_last(""), "");
assert_eq!(s.substring_before_last("Löwe 老虎 Léopard Gepardi"), "");
assert_eq!(s.substring_before_last("v"), s);
}
#[test]
fn test_substring_between() {
let s = "L(=öwe 老虎 L)=éopard Gepardi";
let sub = s.substring_between("", "");
assert_eq!(s, sub);
let sub = s.substring_between("L", "L");
assert_eq!("(=öwe 老虎 ", sub);
let sub = s.substring_between("(=", "");
assert_eq!("öwe 老虎 L)=éopard Gepardi", sub);
let sub = s.substring_between("", ")=");
assert_eq!("L(=öwe 老虎 L", sub);
let sub = s.substring_between("(=", ")=");
assert_eq!("öwe 老虎 L", sub);
let sub = s.substring_between("(=1", ")=");
assert_eq!("", sub);
}
#[test]
fn substrings_between() {
let s = "L(=öwe 老虎 L)=éopard Gepardi";
let sub = s.substrings_between("", "");
assert_eq!(Vec::<Cow<str>>::new(), sub);
let sub = s.substrings_between("L", "L");
assert_eq!(vec!["(=öwe 老虎 "], sub);
let sub = s.substrings_between("(=", "");
assert_eq!(Vec::<Cow<str>>::new(), sub);
let sub = s.substrings_between("", ")=");
assert_eq!(Vec::<Cow<str>>::new(), sub);
let sub = s.substrings_between("(=", ")=");
assert_eq!(vec!["öwe 老虎 L"], sub);
let sub = s.substrings_between("(=1", ")=");
assert_eq!(Vec::<Cow<str>>::new(), sub);
let sub = s.substrings_between("p", "d");
assert_eq!(vec!["ar", "ar"], sub);
}
}