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 { 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 { 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()) ); } }