extern crate regex;
mod utils;
use std::str::FromStr;
use regex::*;
use utils::*;
pub trait PatternMatch {
fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error>;
fn pattern_match(&self, pattern: &str, case_insensitive: bool) -> bool;
fn pattern_match_ci(&self, pattern: &str) -> bool;
fn pattern_match_cs(&self, pattern: &str) -> bool;
}
pub trait PatternReplace {
fn pattern_replace(&self, pattern: &str, replacement: &str, case_insensitive: bool) -> Self where Self:Sized;
fn pattern_replace_result(&self, pattern: &str, replacement: &str,case_insensitive: bool) -> Result<Self, Error> where Self:Sized;
fn pattern_replace_ci(&self, pattern: &str, replacement: &str) -> Self where Self:Sized;
fn pattern_replace_cs(&self, pattern: &str, replacement: &str) -> Self where Self:Sized;
}
pub trait StripCharacters {
fn strip_non_alphanum(&self) -> Self where Self:Sized;
fn strip_non_digits(&self) -> Self where Self:Sized;
fn to_numeric_strings(&self) -> Vec<String>;
fn to_numeric_strings_euro(&self) -> Vec<String>;
fn to_numeric_strings_conditional(&self, enforce_comma_separator: bool) -> Vec<String>;
fn to_numbers_conditional<T: FromStr>(&self, enforce_comma_separator: bool) -> Vec<T>;
fn to_numbers<T: FromStr>(&self) -> Vec<T>;
fn to_numbers_euro<T: FromStr>(&self) -> Vec<T>;
fn correct_numeric_string(&self, enforce_comma_separator: bool) -> Self;
fn to_first_number<T: FromStr + Copy>(&self) -> Option<T>;
fn to_first_number_euro<T: FromStr + Copy>(&self) -> Option<T>;
fn strip_non_numeric(&self) -> Self where Self:Sized;
}
pub trait CharGroupMatch {
fn has_digits(&self) -> bool;
fn has_alphanumeric(&self) -> bool;
fn has_alphabetic(&self) -> bool;
}
pub trait SimpleMatch {
fn starts_with_ci(&self, pattern: &str) -> bool;
fn starts_with_ci_alphanum(&self, pattern: &str) -> bool;
fn ends_with_ci(&self, pattern: &str) -> bool;
fn ends_with_ci_alphanum(&self, pattern: &str) -> bool;
fn contains_ci(&self, pattern: &str) -> bool;
fn contains_ci_alphanum(&self, pattern: &str) -> bool;
}
pub trait IsNumeric {
fn is_numeric(&self) -> bool;
}
impl IsNumeric for String {
fn is_numeric(&self) -> bool {
self.pattern_match(r#"^-?(\d+,)*\d+((\.)\d+)?"#, false)
}
}
pub trait ToSegments {
fn to_segments(&self, separator: &str) -> Vec<Self> where Self:Sized;
fn to_parts(&self, separator: &str) -> Vec<Self> where Self:Sized;
fn to_head(&self, separator: &str) -> Self where Self:Sized;
fn to_first(&self, separator: &str) -> Self where Self:Sized;
fn to_remainder_end(&self, separator: &str) -> Self where Self:Sized;
fn to_last(&self, separator: &str) -> Self where Self:Sized;
fn to_remainder_start(&self, separator: &str) -> Self where Self:Sized;
fn to_end(&self, separator: &str) -> Self where Self:Sized;
fn to_segment(&self, separator: &str, index: i32) -> Option<Self> where Self:Sized;
fn to_inner_segment(&self, groups: &[(&str, i32)]) -> Option<Self> where Self:Sized;
fn to_tail(&self, separator: &str) -> Self where Self:Sized;
fn to_head_tail(&self, separator: &str) -> (Self, Self) where Self:Sized;
fn to_start_end(&self, separator: &str) -> (Self, Self) where Self:Sized;
}
pub trait ToStrings {
fn to_strings(&self) -> Vec<String>;
}
impl<T: ToString> ToStrings for Vec<T> {
fn to_strings(&self) -> Vec<String> {
self.into_iter().map(|s| s.to_string()).collect()
}
}
impl<T: ToString> ToStrings for [T] {
fn to_strings(&self) -> Vec<String> {
self.into_iter().map(|s| s.to_string()).collect::<Vec<String>>()
}
}
pub trait MatchOccurrences {
fn find_matched_indices(&self, pat: &str) -> Vec<usize>;
}
impl MatchOccurrences for String {
fn find_matched_indices(&self, pat: &str) -> Vec<usize> {
self.match_indices(pat).into_iter().map(|pair| pair.0).collect::<Vec<usize>>()
}
}
impl PatternMatch for str {
fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
match build_regex(pattern, case_insensitive) {
Ok(re) => Ok(re.is_match(self)),
Err(error) => Err(error)
}
}
fn pattern_match(&self, pattern: &str, case_insensitive: bool) -> bool {
if let Ok(re) = build_regex(pattern, case_insensitive) {
re.is_match(self)
} else {
false
}
}
fn pattern_match_ci(&self, pattern: &str) -> bool {
self.pattern_match(pattern, true)
}
fn pattern_match_cs(&self, pattern: &str) -> bool {
self.pattern_match(pattern, false)
}
}
impl PatternReplace for String {
fn pattern_replace_result(&self, pattern: &str, replacement: &str, case_insensitive: bool) -> Result<String, Error> {
match build_regex(pattern, case_insensitive) {
Ok(re) => Ok(re.replace_all(self, replacement).to_string()),
Err(error) => Err(error)
}
}
fn pattern_replace(&self, pattern: &str, replacement: &str, case_insensitive: bool) -> String {
self.pattern_replace_result(pattern, replacement, case_insensitive).unwrap_or(self.to_owned())
}
fn pattern_replace_ci(&self, pattern: &str, replacement: &str) -> String {
self.pattern_replace(pattern, replacement, true)
}
fn pattern_replace_cs(&self, pattern: &str, replacement: &str) -> String {
self.pattern_replace(pattern, replacement, false)
}
}
impl StripCharacters for String {
fn strip_non_alphanum(&self) -> String {
self.chars().into_iter().filter(|c| c.is_alphanumeric()).collect::<String>()
}
fn strip_non_digits(&self) -> String {
self.chars().into_iter().filter(|c| c.is_digit(10)).collect::<String>()
}
fn correct_numeric_string(&self, enforce_comma_separator: bool) -> Self {
let commas = self.find_matched_indices(",");
let last_comma_index = commas.last().unwrap_or(&0).to_owned();
let points = self.find_matched_indices(".");
let last_point_index = points.last().unwrap_or(&0).to_owned();
let num_commas = commas.len();
if points.len() > 1 || (last_comma_index > last_point_index && num_commas <= 1) || (enforce_comma_separator && num_commas <= 1) {
if num_commas < 1 {
self.replace(".", "")
} else {
let (main, dec_part) = self.to_start_end(",");
[main.replace(".", ""), dec_part].join(".")
}
} else {
self.to_owned()
}
}
fn to_numeric_strings_conditional(&self, enforce_comma_separator: bool) -> Vec<String> {
let mut prev_char = ' ';
let mut seq_num = 0;
let mut num_string = String::new();
let mut output: Vec<String> = Vec::new();
for component in self.chars() {
let mut is_end = false;
if component.is_digit(10) {
num_string.push(component);
seq_num += 1;
} else if prev_char.is_digit(10) {
match component {
'.' | '․' | ',' => {
if component == ',' {
num_string.push(',');
} else {
num_string.push('.');
}
seq_num = 0;
},
_ => {
is_end = true;
}
}
} else {
is_end = true;
}
if is_end {
if seq_num > 0 {
add_sanitized_numeric_string(&mut output, &num_string.correct_numeric_string(enforce_comma_separator));
num_string = String::new();
}
seq_num = 0;
}
prev_char = component;
}
if num_string.len() > 0 {
add_sanitized_numeric_string(&mut output, &num_string.correct_numeric_string(enforce_comma_separator));
}
output
}
fn to_numeric_strings(&self) -> Vec<String> {
self.to_numeric_strings_conditional(false)
}
fn to_numeric_strings_euro(&self) -> Vec<String> {
self.to_numeric_strings_conditional(true)
}
fn to_numbers_conditional<T: FromStr>(&self, enforce_comma_separator: bool) -> Vec<T> {
self.to_numeric_strings_conditional(enforce_comma_separator).into_iter()
.map(|s| s.parse::<T>())
.filter(|s| s.is_ok())
.map(|s| s.ok().unwrap())
.collect::<Vec<T>>()
}
fn to_numbers<T: FromStr>(&self) -> Vec<T> {
self.to_numbers_conditional(false)
}
fn to_numbers_euro<T: FromStr>(&self) -> Vec<T> {
self.to_numbers_conditional(true)
}
fn to_first_number<T: FromStr + Copy>(&self) -> Option<T> {
if let Some(number) = self.to_numbers::<T>().first() {
Some(*number)
} else {
None
}
}
fn to_first_number_euro<T: FromStr + Copy>(&self) -> Option<T> {
if let Some(number) = self.to_numbers_euro::<T>().first() {
Some(*number)
} else {
None
}
}
fn strip_non_numeric(&self) -> String {
self.to_numeric_strings().join(" ")
}
}
impl CharGroupMatch for str {
fn has_digits(&self) -> bool {
self.chars().any(|c| char::is_digit(c, 10))
}
fn has_alphanumeric(&self) -> bool {
self.chars().any(char::is_alphanumeric)
}
fn has_alphabetic(&self) -> bool {
self.chars().any(char::is_alphabetic)
}
}
impl SimpleMatch for str {
fn starts_with_ci(&self, pattern: &str) -> bool {
self.to_lowercase().starts_with(&pattern.to_lowercase())
}
fn starts_with_ci_alphanum(&self, pattern: &str) -> bool {
self.to_lowercase().strip_non_alphanum().starts_with(&pattern.to_lowercase())
}
fn ends_with_ci(&self, pattern: &str) -> bool {
self.to_lowercase().ends_with(&pattern.to_lowercase())
}
fn ends_with_ci_alphanum(&self, pattern: &str) -> bool {
self.to_lowercase().strip_non_alphanum().ends_with(&pattern.to_lowercase())
}
fn contains_ci(&self, pattern: &str) -> bool {
self.to_lowercase().contains(&pattern.to_lowercase())
}
fn contains_ci_alphanum(&self, pattern: &str) -> bool {
self.to_lowercase().strip_non_alphanum().contains(&pattern.to_lowercase())
}
}
pub trait PatternMatches {
fn pattern_matches_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<bool>, Error>;
fn pattern_matches(&self, pattern: &str, case_insensitive: bool) -> Vec<bool>;
fn pattern_matches_ci(&self, pattern: &str) -> Vec<bool>;
fn pattern_matches_cs(&self, pattern: &str) -> Vec<bool>;
}
impl PatternMatch for [String] {
fn pattern_match_result(&self, pattern: &str, case_insensitive: bool) -> Result<bool, Error> {
match build_regex(pattern, case_insensitive) {
Ok(re) => Ok(self.into_iter().any(|segment| re.is_match(segment))),
Err(error) => Err(error)
}
}
fn pattern_match(&self, pattern: &str, case_insensitive: bool) -> bool {
self.pattern_match_result(pattern, case_insensitive).unwrap_or(false)
}
fn pattern_match_ci(&self, pattern: &str) -> bool {
self.pattern_match(pattern, true)
}
fn pattern_match_cs(&self, pattern: &str) -> bool {
self.pattern_match(pattern, false)
}
}
impl PatternMatches for [String] {
fn pattern_matches_result(&self, pattern: &str, case_insensitive: bool) -> Result<Vec<bool>, Error> {
match build_regex(pattern, case_insensitive) {
Ok(re) => Ok(self.into_iter().map(|segment| re.is_match(segment)).collect::<Vec<bool>>()),
Err(error) => Err(error)
}
}
fn pattern_matches(&self, pattern: &str, case_insensitive: bool) -> Vec<bool> {
match self.pattern_matches_result(pattern, case_insensitive) {
Ok(results) => results,
Err(_error) => self.into_iter().map(|_segment| false).collect::<Vec<bool>>()
}
}
fn pattern_matches_ci(&self, pattern: &str) -> Vec<bool> {
self.pattern_matches(pattern, true)
}
fn pattern_matches_cs(&self, pattern: &str) -> Vec<bool> {
self.pattern_matches(pattern, false)
}
}
impl PatternReplace for Vec<String> {
fn pattern_replace_result(&self, pattern: &str, replacement: &str, case_insensitive: bool) -> Result<Vec<String>, Error> {
match build_regex(pattern, case_insensitive) {
Ok(re) => {
let replacements = self.into_iter().map(|segment| re.replace_all(segment, replacement).to_string()).collect::<Vec<String>>();
Ok(replacements)
},
Err(error) => Err(error)
}
}
fn pattern_replace(&self, pattern: &str, replacement: &str, case_insensitive: bool) -> Vec<String> {
self.pattern_replace_result(pattern, replacement, case_insensitive).unwrap_or(self.to_owned())
}
fn pattern_replace_ci(&self, pattern: &str, replacement: &str) -> Vec<String> {
self.pattern_replace(pattern, replacement, true)
}
fn pattern_replace_cs(&self, pattern: &str, replacement: &str) -> Vec<String> {
self.pattern_replace(pattern, replacement, false)
}
}
pub trait PatternMatchMany {
fn pattern_match_many(&self, patterns: &[&str], case_insensitive: bool) -> bool;
fn pattern_match_many_ci(&self, patterns: &[&str]) -> bool;
fn pattern_match_many_cs(&self, patterns: &[&str]) -> bool;
fn pattern_match_many_mixed(&self, pattern_sets: &[(&str, bool)]) -> bool;
fn pattern_match_many_conditional(&self, pattern_sets: &[(bool, &str, bool)]) -> bool;
fn pattern_match_any(&self, patterns: &[&str], case_insensitive: bool) -> bool;
fn pattern_match_any_ci(&self, patterns: &[&str]) -> bool;
fn pattern_match_any_cs(&self, patterns: &[&str]) -> bool;
fn pattern_match_any_mixed(&self, pattern_sets: &[(&str, bool)]) -> bool;
fn pattern_match_any_conditional(&self, pattern_sets: &[(bool, &str, bool)]) -> bool;
}
pub trait PatternReplaceMany {
fn pattern_replace_pairs(&self, replacement_sets: &[(&str, &str)]) -> Self where Self: Sized;
fn pattern_replace_pairs_ci(&self, replacement_sets: &[(&str, &str)]) -> Self where Self: Sized;
fn pattern_replace_sets(&self, replacement_sets: &[(&str, &str, bool)]) -> Self where Self: Sized;
}
impl PatternMatchMany for str {
fn pattern_match_many(&self, patterns: &[&str], case_insensitive: bool) -> bool {
let mut num_matched = 0usize;
let num_patterns = patterns.len();
for pattern in patterns {
if self.pattern_match(pattern, case_insensitive) {
num_matched += 1;
}
}
num_matched == num_patterns
}
fn pattern_match_many_ci(&self, patterns: &[&str]) -> bool {
self.pattern_match_many(patterns, true)
}
fn pattern_match_many_cs(&self, patterns: &[&str]) -> bool {
self.pattern_match_many(patterns, false)
}
fn pattern_match_many_mixed(&self, pattern_sets: &[(&str, bool)]) -> bool {
let mut num_matched = 0usize;
let num_patterns = pattern_sets.len();
for pair in pattern_sets {
let (pattern, case_insensitive) = *pair;
if self.pattern_match(pattern, case_insensitive) {
num_matched += 1;
}
}
num_matched == num_patterns
}
fn pattern_match_many_conditional(&self, pattern_sets: &[(bool, &str, bool)]) -> bool {
let mut num_matched = 0usize;
let num_patterns = pattern_sets.len();
for pattern_set in pattern_sets {
let (is_positive, pattern, case_insensitive) = *pattern_set;
let is_matched = self.pattern_match(pattern, case_insensitive);
if is_matched == is_positive {
num_matched += 1;
}
}
num_matched == num_patterns
}
fn pattern_match_any(&self, patterns: &[&str], case_insensitive: bool) -> bool {
for pattern in patterns {
if self.pattern_match(pattern, case_insensitive) {
return true;
}
}
false
}
fn pattern_match_any_ci(&self, patterns: &[&str]) -> bool {
self.pattern_match_any(patterns, true)
}
fn pattern_match_any_cs(&self, patterns: &[&str]) -> bool {
self.pattern_match_any(patterns, false)
}
fn pattern_match_any_mixed(&self, pattern_sets: &[(&str, bool)]) -> bool {
for pair in pattern_sets {
let (pattern, case_insensitive) = *pair;
if self.pattern_match(pattern, case_insensitive) {
return true;
}
}
false
}
fn pattern_match_any_conditional(&self, pattern_sets: &[(bool, &str, bool)]) -> bool {
for pattern_set in pattern_sets {
let (is_positive, pattern, case_insensitive) = *pattern_set;
let is_matched = self.pattern_match(pattern, case_insensitive);
if is_matched == is_positive {
return true;
}
}
false
}
}
impl PatternReplaceMany for String {
fn pattern_replace_sets(&self, replacement_sets: &[(&str, &str, bool)]) -> String {
let mut return_string = self.clone();
for replacement_set in replacement_sets {
let (pattern, replacement, case_insensitive) = *replacement_set;
if let Ok(new_string) = return_string.pattern_replace_result(pattern, replacement, case_insensitive) {
return_string = new_string;
}
}
return_string
}
fn pattern_replace_pairs(&self, replacement_pairs: &[(&str, &str)]) -> String {
let mut return_string = self.clone();
for replacement_pair in replacement_pairs {
let (pattern, replacement) = *replacement_pair;
if let Ok(new_string) = return_string.pattern_replace_result(pattern, replacement, false) {
return_string = new_string;
}
}
return_string
}
fn pattern_replace_pairs_ci(&self, replacement_pairs: &[(&str, &str)]) -> String {
let mut return_string = self.clone();
for replacement_pair in replacement_pairs {
let (pattern, replacement) = *replacement_pair;
if let Ok(new_string) = return_string.pattern_replace_result(pattern, replacement, true) {
return_string = new_string;
}
}
return_string
}
}
impl PatternMatchMany for [String] {
fn pattern_match_many(&self, patterns: &[&str], case_insensitive: bool) -> bool {
let mut num_matched = 0usize;
let num_patterns = patterns.len();
for pattern in patterns {
if self.pattern_match(pattern, case_insensitive) {
num_matched += 1;
}
}
num_matched == num_patterns
}
fn pattern_match_many_ci(&self, patterns: &[&str]) -> bool {
self.pattern_match_many(patterns, true)
}
fn pattern_match_many_cs(&self, patterns: &[&str]) -> bool {
self.pattern_match_many(patterns, false)
}
fn pattern_match_many_mixed(&self, pattern_sets: &[(&str, bool)]) -> bool {
let mut num_matched = 0usize;
let num_patterns = pattern_sets.len();
for pair in pattern_sets {
let (pattern, case_insensitive) = *pair;
if self.pattern_match(pattern, case_insensitive) {
num_matched += 1;
}
}
num_matched == num_patterns
}
fn pattern_match_many_conditional(&self, pattern_sets: &[(bool, &str, bool)]) -> bool {
let mut num_matched = 0usize;
let num_patterns = pattern_sets.len();
for pattern_set in pattern_sets {
let (is_positive, pattern, case_insensitive) = *pattern_set;
let is_matched = self.pattern_match(pattern, case_insensitive);
if is_matched == is_positive {
num_matched += 1;
}
}
num_matched == num_patterns
}
fn pattern_match_any(&self, patterns: &[&str], case_insensitive: bool) -> bool {
for pattern in patterns {
if self.pattern_match(pattern, case_insensitive) {
return true;
}
}
false
}
fn pattern_match_any_ci(&self, patterns: &[&str]) -> bool {
self.pattern_match_any(patterns, true)
}
fn pattern_match_any_cs(&self, patterns: &[&str]) -> bool {
self.pattern_match_any(patterns, false)
}
fn pattern_match_any_mixed(&self, pattern_sets: &[(&str, bool)]) -> bool {
for pair in pattern_sets {
let (pattern, case_insensitive) = *pair;
if self.pattern_match(pattern, case_insensitive) {
return true;
}
}
false
}
fn pattern_match_any_conditional(&self, pattern_sets: &[(bool, &str, bool)]) -> bool {
for pattern_set in pattern_sets {
let (is_positive, pattern, case_insensitive) = *pattern_set;
let is_matched = self.pattern_match(pattern, case_insensitive);
if is_matched == is_positive {
return true;
}
}
false
}
}
impl PatternReplaceMany for Vec<String> {
fn pattern_replace_sets(&self, replacement_sets: &[(&str, &str, bool)]) -> Vec<String> {
let mut return_strings = self.clone();
for replacement_set in replacement_sets {
let (pattern, replacement, case_insensitive) = *replacement_set;
if let Ok(new_strings) = return_strings.pattern_replace_result(pattern, replacement, case_insensitive) {
return_strings = new_strings;
}
}
return_strings
}
fn pattern_replace_pairs(&self, replacement_pairs: &[(&str, &str)]) -> Vec<String> {
let mut return_strings = self.clone();
for replacement_pair in replacement_pairs {
let (pattern, replacement) = *replacement_pair;
if let Ok(new_string) = return_strings.pattern_replace_result(pattern, replacement, false) {
return_strings = new_string;
}
}
return_strings
}
fn pattern_replace_pairs_ci(&self, replacement_pairs: &[(&str, &str)]) -> Vec<String> {
let mut return_strings = self.clone();
for replacement_pair in replacement_pairs {
let (pattern, replacement) = *replacement_pair;
if let Ok(new_string) = return_strings.pattern_replace_result(pattern, replacement, true) {
return_strings = new_string;
}
}
return_strings
}
}
impl ToSegments for String {
fn to_parts(&self, separator: &str) -> Vec<String> {
let splitter = self.split(separator);
splitter.into_iter().map(|s| s.to_string()).collect::<Vec<String>>()
}
fn to_segments(&self, separator: &str) -> Vec<String> {
let splitter = self.split(separator);
splitter.into_iter().map(|s| s.to_string()).filter(|s| s.len() > 0).collect::<Vec<String>>()
}
fn to_head(&self, separator: &str) -> String {
if let Some((head, _tail)) = self.split_once(separator) {
head.to_string()
} else {
self.to_owned()
}
}
fn to_last(&self, separator: &str) -> String {
let separator_len = separator.len();
if self.ends_with(separator) && self.len() > separator_len {
let end_index = self.len() - separator_len;
self[0..end_index].to_string().to_end(separator)
} else {
self.to_end(separator)
}
}
fn to_end(&self, separator: &str) -> String {
let parts = self.to_parts(separator);
if parts.len() > 0 {
parts.last().unwrap_or(self).to_owned()
} else {
self.to_owned()
}
}
fn to_tail(&self, separator: &str) -> String {
let parts = self.to_parts(separator);
let num_parts = parts.len();
if num_parts > 0 {
parts[1..num_parts].join(separator)
} else {
self.to_owned()
}
}
fn to_first(&self, separator: &str) -> String {
let separator_len = separator.len();
if self.starts_with(separator) && self.len() > separator_len {
self[separator_len..self.len()].to_string().to_head(separator)
} else {
self.to_head(separator)
}
}
fn to_remainder_end(&self, separator: &str) -> String {
let separator_len = separator.len();
if self.starts_with(separator) && self.len() > separator_len {
self[separator_len..].to_string().to_tail(separator)
} else {
self.to_tail(separator)
}
}
fn to_remainder_start(&self, separator: &str) -> String {
let separator_len = separator.len();
if self.ends_with(separator) && self.len() > separator_len {
let end_index = self.len() - separator_len;
self[0..end_index].to_string().to_tail(separator)
} else {
self.to_tail(separator)
}
}
fn to_segment(&self, separator: &str, index: i32) -> Option<String> {
let parts = self.to_segments(separator);
let num_parts = parts.len();
let target_index = if index >= 0 { index as usize } else { (num_parts as i32 + index) as usize };
if target_index < num_parts {
if let Some(segment) = parts.get(target_index) {
Some(segment.to_owned())
} else {
None
}
} else {
None
}
}
fn to_inner_segment(&self, groups: &[(&str, i32)]) -> Option<String> {
if groups.len() > 0 {
let mut matched: Option<String> = None;
let mut current_string = self.clone();
for group in groups {
if current_string.len() > 0 {
let (separator, index) = group;
matched = current_string.to_segment(*separator, *index);
current_string = matched.clone().unwrap_or("".to_string());
}
}
matched
} else {
None
}
}
fn to_head_tail(&self, separator: &str) -> (String, String) {
if let Some((head, tail)) = self.split_once(separator) {
(head.to_string(), tail.to_string())
} else {
("".to_owned(), self.to_owned())
}
}
fn to_start_end(&self, separator: &str) -> (String, String) {
let parts = self.to_parts(separator);
let last_part = "".to_string();
let num_parts = parts.len();
if num_parts > 0 {
let end_index = num_parts - 1;
let start = parts[0..end_index].join(separator);
let end = self.to_end(separator);
(start, end)
} else {
(self.to_owned(), last_part)
}
}
}