use std::collections::HashSet;
pub trait StringUtil {
fn is_quoted(&self) -> bool;
fn substring(&self, start: i64, end: i64) -> &str;
fn unquote(&self, unescape: bool, quote_set: Option<&HashSet<char>>) -> String;
fn url_to_nodes(&self) -> Vec<&str>;
fn ensure_prefix(&self, prefix: &str) -> String;
fn ensure_suffix(&self, suffix: &str) -> String;
fn drop_prefix(&self, prefix: &str) -> String;
fn drop_suffix(&self, suffix: &str) -> String;
fn join_path_segment(&self, segment: &str) -> String;
fn join_path_segments(&self, segments: Vec<&str>) -> String;
}
impl StringUtil for str {
#[inline]
fn is_quoted(&self) -> bool {
(self.starts_with('\'') || self.starts_with('"'))
&& self.chars().next() == self.chars().last()
}
fn unquote(&self, unescape: bool, quote_set: Option<&HashSet<char>>) -> String {
if self.is_empty() || self.len() < 2 {
return self.to_string();
}
let start = self.chars().next().unwrap();
let end = self.chars().last().unwrap();
if start != end {
return self.to_string();
}
let default_quote_chars = HashSet::from(['"', '\'']);
let quote_set = quote_set.unwrap_or(&default_quote_chars);
if !quote_set.contains(&start) {
return self.to_string();
}
let result = self.substring(1, -1);
if unescape {
let escaped_quote = format!("\\{}", start);
return result.replace(&escaped_quote, &start.to_string());
}
result.to_string()
}
fn substring(&self, start: i64, end: i64) -> &str {
let _start = if start < 0 {
(self.len() as i64 + start) as usize
} else {
start as usize
};
let _end = if end <= 0 {
(self.len() as i64 + end) as usize
} else {
end as usize
};
if _start > _end {
""
} else {
&self[_start.._end]
}
}
#[inline]
fn url_to_nodes(&self) -> Vec<&str> {
let mut nodes = vec!["/"];
nodes.extend(self.split('/').filter(|s| !s.is_empty()));
nodes
}
#[inline]
fn ensure_prefix(&self, prefix: &str) -> String {
let mut result = self.to_string();
if !self.starts_with(prefix) {
result = format!("{}{}", prefix, self);
}
result
}
#[inline]
fn ensure_suffix(&self, suffix: &str) -> String {
let mut result = self.to_string();
if !self.ends_with(suffix) {
result.push_str(suffix);
}
result
}
#[inline]
fn drop_prefix(&self, prefix: &str) -> String {
if self.starts_with(prefix) {
self.substring(prefix.len() as i64, 0).to_string()
} else {
self.to_string()
}
}
#[inline]
fn drop_suffix(&self, suffix: &str) -> String {
if self.ends_with(suffix) {
self.substring(0, -(suffix.len() as i64)).to_string()
} else {
self.to_string()
}
}
#[inline]
fn join_path_segment(&self, segment: &str) -> String {
let mut url = self.ensure_suffix("/");
url.push_str(&segment.drop_prefix("/"));
url
}
fn join_path_segments(&self, segments: Vec<&str>) -> String {
let mut url: String = self.to_string();
for segment in segments {
url = url.join_path_segment(segment);
}
url
}
}