bt_string_utils 0.3.0

Basic string operations
Documentation
#[cfg(test)]
mod sub_strings_test{
    use bt_string_utils::finder::get_first_occurrance;
use bt_string_utils::splitter::get_first_of_split;


    #[test]
    fn test_first_occurance(){
        let content = get_first_occurrance("First:Second:Third",":");
        println!("Content {:?}",&content);
        assert_eq!(content,"First");
    }

    #[test]
    fn test_first_occurance_no_first(){
        let content = get_first_occurrance("First:Second:Third","*");
        println!("Content {:?}",&content);
        assert_eq!(content,"");
    }

    #[test]
    fn test_first_split(){
        let content = get_first_of_split("First:Second:Third",":");
        println!("Content {:?}",&content);
        assert_eq!(content, ("First".to_owned(),"Second:Third".to_owned()));
    }

    #[test]
    fn test_first_split_no_split(){
        let content = get_first_of_split("First:Second:Third","*");
        println!("Content {:?}",&content);
        assert_eq!(content, ("First:Second:Third".to_owned(),"".to_owned()));
    }
}

//**************/
//UNIT TEST
//*************/
#[cfg(test)]
mod strings_test{
    use bt_string_utils::splitter::find_value_by_key;


    #[test]
    fn find_value_by_key_exist_test(){
        let v = vec!["k1=a".to_owned(),"k2=b".to_owned(),"k3=c".to_string()];
        assert_eq!(find_value_by_key(&v, "k3").unwrap(),"c");
    }

    #[test]
    fn find_value_by_key_nofound_test(){
        let v = vec!["k1=a".to_owned(),"k2=b".to_owned(),"k3=c".to_string()];
        assert_eq!(find_value_by_key(&v, "k5"),None);
    }    
}

#[cfg(test)]
mod removed_tests {
    use bt_string_utils::cleanser::{RemoveLocationEnum, remove_char};


    #[test]
    fn test_remove_first_char() {
        assert_eq!(remove_char(RemoveLocationEnum::Begin, &"hello".to_string(), 'h'), "ello");
        assert_eq!(remove_char(RemoveLocationEnum::Begin, &"rust".to_string(), 'r'), "ust");
    }

    #[test]
    fn test_remove_last_char() {
        assert_eq!(remove_char(RemoveLocationEnum::End, &"world!".to_string(), '!'), "world");
        assert_eq!(remove_char(RemoveLocationEnum::End, &"test".to_string(), 't'), "tes");
    }

    #[test]
    fn test_no_removal() {
        assert_eq!(remove_char(RemoveLocationEnum::Begin, &"rust".to_string(), 'x'), "rust");
        assert_eq!(remove_char(RemoveLocationEnum::End, &"mars".to_string(), 'z'), "mars");
    }
}

#[cfg(test)]
mod rand_string_tests {
    use bt_string_utils::generate_url_safe_string;

    #[test]
    fn test_generate_string_length() {
        let length = 16;
        let result = generate_url_safe_string(length);
        assert_eq!(result.len(), length, "Generated string should be {} characters long", length);
    }

    #[test]
    fn test_generate_string_is_alphanumeric() {
        let result = generate_url_safe_string(20);
        assert!(result.chars().all(|c| c.is_ascii_alphanumeric()), "Generated string should contain only alphanumeric characters");
        assert_eq!(result.len(),20);
    }

    #[test]
    fn test_generate_string_with_zero_length() {
        let result = generate_url_safe_string(0);
        assert_eq!(result.len(), 0, "Generated string for length 0 should be an empty string");
    }

    #[test]
    fn test_generate_string_uniqueness() {
        let result1 = generate_url_safe_string(10);
        let result2 = generate_url_safe_string(10);
        assert_ne!(result1, result2, "Two generated strings should be different");
    }

}

#[cfg(test)]
mod string_match_tests {
    use bt_string_utils::finder::contains_whole_word;


    #[test]
    fn test_exact_match() {
        let text = "this is a target match";
        assert!(contains_whole_word(text, "target"));
    }

    #[test]
    fn test_substring_match_should_false() {
        let text = "this is a targeted match";
        assert!(!contains_whole_word(text, "target"));
    }

    #[test]
    fn test_hyphenated_word_should_false() {
        let text = "no-target";
        assert!(!contains_whole_word(text, "target"));
    }

    #[test]
    fn test_hyphenated_word_should_vs_hyphened_true() {
        let text = "This no-target is match";
        assert!(contains_whole_word(text, "no-target"));
    }

    #[test]
    fn test_word_at_start() {
        let text = "target is the goal";
        assert!(contains_whole_word(text, "target"));
    }

    #[test]
    fn test_word_at_end() {
        let text = "the goal is target";
        assert!(contains_whole_word(text, "target"));
    }

    #[test]
    fn test_multiple_occurrences() {
        let text = "target target target";
        assert!(contains_whole_word(text, "target"));
    }

    #[test]
    fn test_case_sensitive() {
        let text = "Target is here";
        assert!(!contains_whole_word(text, "target")); // case-sensitive by default
    }

    #[test]
    fn test_empty_haystack() {
        let text = "";
        assert!(!contains_whole_word(text, "target"));
    }

    #[test]
    fn test_empty_word() {
        let text = "some text here";
        assert!(!contains_whole_word(text, ""));
    }
}

#[cfg(test)]
mod string_break_tests {
    use bt_string_utils::splitter::split_upto_n_by_word;


    #[test]
    fn splits_simple_ascii() {
        let s = "one two three four";
        let parts = split_upto_n_by_word(s, 2);
        assert_eq!(parts, vec!["one two", " three four"]);
    }

    #[test]
    fn splits_unicode_words() {
        let s = "Hello 🙂 World from Rust";
        let parts = split_upto_n_by_word(s, 3);
        assert_eq!(parts, vec!["Hello", " 🙂 World", " from Rust"]);
    }

    #[test]
    fn does_not_split_inside_emoji() {
        let s = "🙂🙂 🙂🙂 🙂🙂";
        let parts = split_upto_n_by_word(s, 3);
        assert_eq!(parts, vec!["🙂🙂", " 🙂🙂", " 🙂🙂"]);
    }

    #[test]
    fn n_larger_than_word_count_preserves_spaces() {
        let s = "Hello 🙂 World.";
        let parts = split_upto_n_by_word(s, 10);
        assert_eq!(parts, vec!["Hello", " 🙂", " World."]);
    }

    #[test]
    fn n_equals_one_preserves_all() {
        let s = "  Hello   🙂   World  ";
        let parts = split_upto_n_by_word(s, 1);
        assert_eq!(parts, vec!["  Hello   🙂   World"]); // or just vec![s]
    }

    #[test]
    fn empty_string_returns_empty_vec() {
        let s = "";
        let parts = split_upto_n_by_word(s, 5);
        assert!(parts.is_empty());
    }

    #[test]
    fn multiple_spaces_are_preserved() {
        let s = "Hello   world   from   Rust";
        let parts = split_upto_n_by_word(s, 2);
        assert_eq!(parts, vec!["Hello   world", "   from   Rust"]);
    }

    #[test]
    fn new_line_char_preserved() {
        let s = "Hello   world\nfrom   Rust";
        let parts = split_upto_n_by_word(s, 2);
        assert_eq!(parts, vec!["Hello   world", "\nfrom   Rust"]);
    }

    #[test]
    fn punctuation_stays_with_word() {
        let s = "Hello, world!";
        let parts = split_upto_n_by_word(s, 2);
        assert_eq!(parts, vec!["Hello,"," world!",]);
    }

    #[test]
    fn punctuation_never_becomes_its_own_group() {
        let s = "Hello, world!";
        let parts = split_upto_n_by_word(s, 3);
        assert_eq!(parts, vec![
            "Hello,",
            " world!",
        ]);
    }

    #[test]
    fn whitespace_never_becomes_its_own_group() {
        let s = "Hello   world";
        let parts = split_upto_n_by_word(s, 3);
        assert_eq!(parts, vec![
            "Hello",
            "   world",
        ]);
    }
}

#[cfg(test)]
mod remove_tags_tests {
    use bt_string_utils::cleanser::remove_tags;

   

    #[test]
    fn empty_string() {
        let s = String::new();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "");
    }

    #[test]
    fn whitespace_only() {
        let s = "     ".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "     ");
    }

    #[test]
    fn no_tags() {
        let s = "hello world".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "hello world");
    }

    #[test]
    fn single_tag() {
        let s = "a <custom>xxx</custom> b".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "a  b");
    }

    #[test]
    fn multiple_tags() {
        let s = "1 <custom>a</custom> 2 <custom>b</custom> 3".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "1  2  3");
    }

    #[test]
    fn nested_tags() {
        // Your function removes from first <custom> to first </custom>
        let s = "<custom>a <custom>b</custom> c</custom> END".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, " END");
    }

    #[test]
    fn missing_closing_tag() {
        let s = "before <custom>no end".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        // Everything after <custom> is dropped
        assert_eq!(r, "before ");
    }

    #[test]
    fn utf8_characters() {
        let s = "héllo <custom>remove</custom> wørld 🌍".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "héllo  wørld 🌍");
    }

    #[test]
    fn tag_at_start() {
        let s = "<custom>abc</custom>xyz".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "xyz");
    }

    #[test]
    fn tag_at_end() {
        let s = "xyz<custom>abc</custom>".to_string();
        let r = remove_tags(&s, "<custom>","</custom>");
        assert_eq!(r, "xyz");
    }
}