ferroui_base/media/
string_splitter.rs1use crate::utilities::FormatError;
4
5#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7pub struct StringSplitOptions {
8 pub remove_empty_entries: bool,
9 pub trim_entries: bool,
10}
11
12impl StringSplitOptions {
13 pub const NONE: Self = Self { remove_empty_entries: false, trim_entries: false };
14 pub const REMOVE_EMPTY_ENTRIES: Self = Self { remove_empty_entries: true, trim_entries: false };
15 pub const TRIM_ENTRIES: Self = Self { remove_empty_entries: false, trim_entries: true };
16}
17
18pub fn split_respecting_brackets<'a>(
23 s: &'a str,
24 separators: &[char],
25 opening_bracket: char,
26 closing_bracket: char,
27 options: StringSplitOptions,
28) -> Result<Vec<&'a str>, FormatError> {
29 assert!(
30 opening_bracket != closing_bracket,
31 "Opening bracket and closing bracket cannot be the same character '{opening_bracket}'."
32 );
33
34 let mut result = Vec::new();
35 let mut depth = 0usize;
36 let mut seg_start = 0usize;
37
38 let mut process_segment = |start: usize, end: usize| {
39 let mut segment = &s[start..end];
40 if options.trim_entries {
41 segment = segment.trim();
42 }
43 if !segment.is_empty() || !options.remove_empty_entries {
44 result.push(segment);
45 }
46 };
47
48 for (i, ch) in s.char_indices() {
49 if ch == opening_bracket {
50 depth += 1;
51 } else if ch == closing_bracket {
52 if depth == 0 {
53 return Err(FormatError::from_string(format!(
54 "Unmatched closing bracket '{closing_bracket}' at position {i}."
55 )));
56 }
57 depth -= 1;
58 } else if separators.contains(&ch) {
59 if depth != 0 {
60 continue;
61 }
62 process_segment(seg_start, i);
63 seg_start = i + ch.len_utf8();
64 }
65 }
66
67 if depth != 0 {
68 return Err(FormatError::from_string(format!(
69 "Unmatched opening bracket '{opening_bracket}' in input string."
70 )));
71 }
72
73 process_segment(seg_start, s.len());
75 Ok(result)
76}