use stfu8;
#[derive(Clone, Copy, PartialEq)]
enum PointType {
Length,
Position,
}
#[derive(Clone, Copy, PartialEq)]
enum CharType {
Simple,
Grapheme,
}
fn get_chars(subject: &str, start: usize, end: usize) -> String {
match subject.len() {
0 => subject.to_string(),
_ => crate::split::chars(subject)[start..end].join(""),
}
}
fn get_subject_length(
subject: &str,
position: usize,
point_type: PointType,
char_type: CharType,
) -> usize {
let subject_len = crate::count::count_graphemes(subject);
let position_substruction = match point_type {
PointType::Length => 0,
PointType::Position => 1,
};
let is_out_of_bounds = match char_type {
CharType::Simple => position > subject_len,
CharType::Grapheme => position >= subject_len,
};
if is_out_of_bounds {
subject_len - position_substruction
} else {
position
}
}
#[derive(Clone, Copy, PartialEq)]
enum ReturnType {
AfterNormal,
AfterLast,
BeforeNormal,
BeforeLast,
}
fn return_after_or_before_and_after_last_or_before_last(
subject: &str,
search: &str,
return_type: ReturnType,
) -> String {
let start_position = match return_type {
ReturnType::AfterNormal | ReturnType::BeforeNormal => {
crate::index::index_of(subject, search, 0)
}
ReturnType::AfterLast | ReturnType::BeforeLast => {
crate::index::last_index_of(subject, search, 0)
}
} as isize;
if start_position == -1 {
return "".to_owned();
}
let the_length = crate::count::count(search) as isize;
let chop_start_position = match return_type {
ReturnType::AfterNormal | ReturnType::AfterLast => start_position + the_length,
ReturnType::BeforeNormal | ReturnType::BeforeLast => 0,
};
let chop_end_position = match return_type {
ReturnType::AfterNormal | ReturnType::AfterLast => 0,
ReturnType::BeforeNormal | ReturnType::BeforeLast => start_position,
};
crate::chop::slice(subject, chop_start_position, chop_end_position)
}
pub fn after(subject: &str, search: &str) -> String {
match subject.len() {
0 => "".to_string(),
_ => return_after_or_before_and_after_last_or_before_last(
subject,
search,
ReturnType::AfterNormal,
),
}
}
pub fn after_last(subject: &str, search: &str) -> String {
match subject.len() {
0 => "".to_string(),
_ => return_after_or_before_and_after_last_or_before_last(
subject,
search,
ReturnType::AfterLast,
),
}
}
pub fn before(subject: &str, search: &str) -> String {
match subject.len() {
0 => "".to_string(),
_ => return_after_or_before_and_after_last_or_before_last(
subject,
search,
ReturnType::BeforeNormal,
),
}
}
pub fn before_last(subject: &str, search: &str) -> String {
match subject.len() {
0 => "".to_string(),
_ => return_after_or_before_and_after_last_or_before_last(
subject,
search,
ReturnType::BeforeLast,
),
}
}
pub fn char_at(subject: &str, position: usize) -> String {
let the_position = get_subject_length(subject, position, PointType::Position, CharType::Simple);
get_chars(subject, the_position, the_position + 1)
}
pub fn code_point_at(subject: &str, position: usize) -> Vec<u16> {
if subject.is_empty() {
return vec![];
}
let grapheme = grapheme_at(subject, position);
crate::split::code_points(&grapheme)
}
pub fn first(subject: &str, length: usize) -> String {
let the_length = get_subject_length(subject, length, PointType::Length, CharType::Simple);
match length {
0 => "".to_string(),
_ => get_chars(subject, 0, the_length),
}
}
pub fn foreign_key(subject: &str) -> String {
match subject.len() {
0 => subject.to_string(),
_ => {
let safe_string = if subject.contains("::") {
let split_string: Vec<&str> = subject.split("::").collect();
split_string[split_string.len() - 1]
} else {
subject
};
let snake_cased: String = crate::case::snake_case(safe_string);
if snake_cased.ends_with("_id") {
snake_cased
} else {
format!("{}{}", snake_cased, "_id")
}
}
}
}
pub fn grapheme_at(subject: &str, position: usize) -> String {
let subject_len = crate::count::count_graphemes(subject);
match subject_len {
0 => subject.to_string(),
_ => {
let the_position =
get_subject_length(subject, position, PointType::Position, CharType::Grapheme);
crate::split::graphemes(subject)[the_position].to_string()
}
}
}
pub fn last(subject: &str, length: usize) -> String {
match length {
0 => "".to_string(),
_ => {
let subject_length = crate::split::chars(subject).len();
let the_length =
get_subject_length(subject, length, PointType::Length, CharType::Grapheme);
get_chars(subject, subject_length - the_length, subject_length)
}
}
}
pub fn prune(subject: &str, length: usize, end: &str) -> String {
if length == 0 {
return "".to_string();
}
let mut suffix = match end {
"" => "...",
_ => end,
};
let subject_chars = crate::split::chars(subject);
let subject_length = subject_chars.len();
let end_length = crate::split::chars(suffix).len();
let position_end = if subject_length <= length {
suffix = "";
subject_length
} else {
let string_length = length - end_length;
let mut char_indices = subject_chars.iter();
let mut end_position = 0;
let mut current_position = 0;
#[derive(Clone, Copy, PartialEq)]
enum WordMode {
Spaces,
Words,
}
let mut mode = WordMode::Words;
while current_position <= string_length {
let next_char = char_indices.next();
match next_char {
Some(c) => {
let mut current_char = String::new();
current_char.push_str(c);
if crate::utils::WHITESPACE.contains(¤t_char)
|| crate::utils::PUNCTUATION.contains(¤t_char)
{
if mode == WordMode::Words {
end_position = if current_position > 0 {
current_position
} else {
0
};
mode = WordMode::Spaces;
}
} else if mode == WordMode::Spaces {
mode = WordMode::Words;
}
}
None => {
return subject.to_string();
}
}
current_position += 1;
}
end_position
};
format!("{}{}", get_chars(subject, 0, position_end), suffix)
}
#[derive(Clone, Copy, PartialEq)]
enum CutType {
StartsWith,
EndsWith,
}
fn remove_prefix_or_suffix(subject: &str, substring: &str, cut_type: CutType) -> String {
let substring_len = crate::count::count(substring);
match substring_len {
0 => subject.to_owned(),
_ => {
if cut_type == CutType::StartsWith {
if crate::query::starts_with(subject, substring) {
crate::chop::after(subject, substring)
} else {
subject.to_owned()
}
} else if crate::query::ends_with(subject, substring) {
crate::chop::before_last(subject, substring)
} else {
subject.to_owned()
}
}
}
}
pub fn removeprefix(subject: &str, prefix: &str) -> String {
match subject.len() {
0 => subject.to_owned(),
_ => remove_prefix_or_suffix(subject, prefix, CutType::StartsWith),
}
}
pub fn removesuffix(subject: &str, prefix: &str) -> String {
match subject.len() {
0 => subject.to_owned(),
_ => remove_prefix_or_suffix(subject, prefix, CutType::EndsWith),
}
}
pub fn slice(subject: &str, start: isize, end: isize) -> String {
let subject_length = crate::split::chars(subject).len();
let position_start = calculate_position(subject_length, start, true);
let position_end = calculate_position(subject_length, end, false);
fn calculate_position(length: usize, x: isize, start: bool) -> usize {
if x < 0 {
let pos = length as isize - x.abs();
if pos < 0 {
0
} else {
pos as usize
}
} else if x == 0 {
if start {
0
} else {
length
}
} else if x > length as isize {
length
} else {
x as usize
}
}
get_chars(subject, position_start, position_end)
}
pub fn substr(subject: &str, start: usize, length: usize) -> String {
let subject_length = crate::split::chars(subject).len();
if start >= subject_length {
return "".to_string();
}
let position_end = match length {
0 => subject_length,
_ => {
let to_position = start + length;
if to_position > subject_length {
subject_length
} else {
to_position
}
}
};
if start >= position_end {
return "".to_string();
}
get_chars(subject, start, position_end)
}
pub fn substring(subject: &str, start: usize, end: usize) -> String {
let subject_length = crate::split::chars(subject).len();
if start >= subject_length {
return "".to_string();
}
let position_end = match end {
0 => subject_length,
_ => {
if end > subject_length {
subject_length
} else {
end
}
}
};
if start > position_end {
return "".to_string();
}
get_chars(subject, start, position_end)
}
pub fn truncate(subject: &str, length: usize, end: &str) -> String {
if length == 0 {
return "".to_string();
}
let mut suffix = match end {
"" => "...",
_ => end,
};
let subject_length = crate::split::chars(subject).len();
let end_length = crate::split::chars(suffix).len();
let position_end = if subject_length < length || length < end_length {
suffix = "";
subject_length
} else {
length - end_length
};
format!("{}{}", get_chars(subject, 0, position_end), suffix)
}
pub fn max(subject: &str) -> String {
if subject.is_empty() {
return "".to_owned();
}
min_max(subject, MinMaxType::Max)
}
pub fn min(subject: &str) -> String {
if subject.is_empty() {
return "".to_owned();
}
min_max(subject, MinMaxType::Min)
}
#[derive(Clone, Copy, PartialEq)]
enum MinMaxType {
Min,
Max,
}
fn min_max(subject: &str, search_type: MinMaxType) -> String {
if subject.is_empty() {
return "".to_owned();
}
let code_points = crate::split::code_points(subject);
let min_max = match search_type {
MinMaxType::Max => code_points.iter().max(),
MinMaxType::Min => code_points.iter().min(),
};
match min_max {
None => "".to_owned(),
Some(x) => stfu8::encode_u16(&[*x]),
}
}
pub fn limit_words(subject: &str, number: usize, end: &str) -> String {
if number == 0 {
return "".to_string();
}
let mut suffix = match end {
"" => "...",
_ => end,
};
let mut subject_words: Vec<&str> = subject.split_ascii_whitespace().collect();
let fragment = if subject_words.len() <= number {
suffix = "";
subject.to_owned()
} else {
subject_words.truncate(number);
let result = subject_words.join(" ");
crate::manipulate::trim_right(&result, crate::utils::PUNCTUATION)
};
format!("{}{}", fragment, suffix)
}