Files
IronCalc/base/src/functions/text_util.rs
2023-11-20 10:46:19 +01:00

197 lines
5.7 KiB
Rust

pub(crate) enum Case {
Sensitive,
Insensitive,
}
/// Finds the text after the occurrence instance of 'search_for' in text
pub(crate) fn text_after(
text: &str,
delimiter: &str,
instance_num: i32,
match_mode: Case,
) -> Option<String> {
if let Some((_, right)) = match_text(text, delimiter, instance_num, match_mode) {
return Some(text[right..].to_string());
};
None
}
pub(crate) fn text_before(
text: &str,
delimiter: &str,
instance_num: i32,
match_mode: Case,
) -> Option<String> {
if let Some((left, _)) = match_text(text, delimiter, instance_num, match_mode) {
return Some(text[..left].to_string());
};
None
}
pub(crate) fn substitute(text: &str, old_text: &str, new_text: &str, instance_num: i32) -> String {
if let Some((left, right)) = match_text(text, old_text, instance_num, Case::Sensitive) {
return format!("{}{}{}", &text[..left], new_text, &text[right..]);
};
text.to_string()
}
fn match_text(
text: &str,
delimiter: &str,
instance_num: i32,
match_mode: Case,
) -> Option<(usize, usize)> {
match match_mode {
Case::Sensitive => {
if instance_num > 0 {
text_sensitive(text, delimiter, instance_num)
} else {
text_sensitive_reverse(text, delimiter, -instance_num)
}
}
Case::Insensitive => {
if instance_num > 0 {
text_sensitive(
&text.to_lowercase(),
&delimiter.to_lowercase(),
instance_num,
)
} else {
text_sensitive_reverse(
&text.to_lowercase(),
&delimiter.to_lowercase(),
-instance_num,
)
}
}
}
}
fn text_sensitive(text: &str, delimiter: &str, instance_num: i32) -> Option<(usize, usize)> {
let mut byte_index = 0;
let mut local_index = 1;
// delimiter length in bytes
let delimiter_len = delimiter.len();
for c in text.chars() {
if text[byte_index..].starts_with(delimiter) {
if local_index == instance_num {
return Some((byte_index, byte_index + delimiter_len));
} else {
local_index += 1;
}
}
byte_index += c.len_utf8();
}
None
}
fn text_sensitive_reverse(
text: &str,
delimiter: &str,
instance_num: i32,
) -> Option<(usize, usize)> {
let text_len = text.len();
let mut byte_index = text_len;
let mut local_index = 1;
let delimiter_len = delimiter.len();
for c in text.chars().rev() {
if text[byte_index..].starts_with(delimiter) {
if local_index == instance_num {
return Some((byte_index, byte_index + delimiter_len));
} else {
local_index += 1;
}
}
byte_index -= c.len_utf8();
}
None
}
#[cfg(test)]
mod tests {
use crate::functions::text_util::Case;
use super::{text_after, text_before};
#[test]
fn test_text_after_sensitive() {
assert_eq!(
text_after("One element", "ele", 1, Case::Sensitive),
Some("ment".to_string())
);
assert_eq!(
text_after("One element", "e", 1, Case::Sensitive),
Some(" element".to_string())
);
assert_eq!(
text_after("One element", "e", 4, Case::Sensitive),
Some("nt".to_string())
);
assert_eq!(text_after("One element", "e", 5, Case::Sensitive), None);
assert_eq!(
text_after("長壽相等!", "", 1, Case::Sensitive),
Some("等!".to_string())
);
}
#[test]
fn test_text_before_sensitive() {
assert_eq!(
text_before("One element", "ele", 1, Case::Sensitive),
Some("One ".to_string())
);
assert_eq!(
text_before("One element", "e", 1, Case::Sensitive),
Some("On".to_string())
);
assert_eq!(
text_before("One element", "e", 4, Case::Sensitive),
Some("One elem".to_string())
);
assert_eq!(text_before("One element", "e", 5, Case::Sensitive), None);
assert_eq!(
text_before("長壽相等!", "", 1, Case::Sensitive),
Some("長壽".to_string())
);
}
#[test]
fn test_text_after_insensitive() {
assert_eq!(
text_after("One element", "eLe", 1, Case::Insensitive),
Some("ment".to_string())
);
assert_eq!(
text_after("One element", "E", 1, Case::Insensitive),
Some(" element".to_string())
);
assert_eq!(
text_after("One element", "E", 4, Case::Insensitive),
Some("nt".to_string())
);
assert_eq!(text_after("One element", "E", 5, Case::Insensitive), None);
assert_eq!(
text_after("長壽相等!", "", 1, Case::Insensitive),
Some("等!".to_string())
);
}
#[test]
fn test_text_before_insensitive() {
assert_eq!(
text_before("One element", "eLe", 1, Case::Insensitive),
Some("One ".to_string())
);
assert_eq!(
text_before("One element", "E", 1, Case::Insensitive),
Some("On".to_string())
);
assert_eq!(
text_before("One element", "E", 4, Case::Insensitive),
Some("One elem".to_string())
);
assert_eq!(text_before("One element", "E", 5, Case::Insensitive), None);
assert_eq!(
text_before("長壽相等!", "", 1, Case::Insensitive),
Some("長壽".to_string())
);
}
}