use crate::error::SplitCompositeError;
pub fn split_composite_weight(
mut s: &str,
separator: char,
open_paren: char,
close_paren: char,
) -> Result<Vec<&str>, SplitCompositeError> {
s = s.trim();
if s.starts_with(open_paren) && s.ends_with(close_paren) {
let mut depth = 0;
let mut is_single_group = true;
for (i, c) in s.char_indices() {
if c == open_paren {
depth += 1;
} else if c == close_paren {
depth -= 1;
if depth == 0 && i != s.len() - close_paren.len_utf8() {
is_single_group = false;
break;
}
}
}
if is_single_group && depth == 0 {
s = s[open_paren.len_utf8()..s.len() - close_paren.len_utf8()].trim();
}
}
let mut parts = Vec::new();
let mut depth = 0;
let mut start = 0;
for (i, c) in s.char_indices() {
if c == open_paren {
depth += 1;
} else if c == close_paren {
if depth == 0 {
return Err(SplitCompositeError::UnmatchedCloseParenthesis { offset: i });
}
depth -= 1;
} else if c == separator && depth == 0 {
parts.push(s[start..i].trim());
start = i + c.len_utf8();
}
}
if depth != 0 {
return Err(SplitCompositeError::UnmatchedOpenParenthesis);
}
parts.push(s[start..].trim());
Ok(parts)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_separator_inside_parentheses_does_not_split() {
assert_eq!(
split_composite_weight("(1,2),(3,4)", ',', '(', ')').unwrap(),
vec!["(1,2)", "(3,4)"]
);
assert_eq!(
split_composite_weight("((1,2),3),4", ',', '(', ')').unwrap(),
vec!["((1,2),3)", "4"]
);
}
#[test]
fn outer_parentheses_are_stripped_only_when_they_wrap_everything() {
assert_eq!(
split_composite_weight("(1,2)", ',', '(', ')').unwrap(),
vec!["1", "2"]
);
assert_eq!(
split_composite_weight("(1),(2)", ',', '(', ')').unwrap(),
vec!["(1)", "(2)"]
);
}
#[test]
fn whitespace_around_elements_is_trimmed() {
assert_eq!(
split_composite_weight(" 1 , 2 ", ',', '(', ')').unwrap(),
vec!["1", "2"]
);
assert_eq!(
split_composite_weight(" ( 1 , 2 ) ", ',', '(', ')').unwrap(),
vec!["1", "2"]
);
}
#[test]
fn an_empty_element_is_preserved_rather_than_dropped() {
assert_eq!(
split_composite_weight("1,,2", ',', '(', ')').unwrap(),
vec!["1", "", "2"]
);
assert_eq!(split_composite_weight("", ',', '(', ')').unwrap(), vec![""]);
}
#[test]
fn a_different_separator_and_bracket_pair_work() {
assert_eq!(
split_composite_weight("[1|2]", '|', '[', ']').unwrap(),
vec!["1", "2"]
);
}
#[test]
fn unbalanced_parentheses_are_reported_with_their_position() {
assert_eq!(
split_composite_weight("1,2)", ',', '(', ')'),
Err(SplitCompositeError::UnmatchedCloseParenthesis { offset: 3 })
);
assert_eq!(
split_composite_weight("(1,(2)", ',', '(', ')'),
Err(SplitCompositeError::UnmatchedOpenParenthesis)
);
}
#[test]
fn test_split_composite_weight() {
let text = "1.0, 2.0, 3.0";
let parts = split_composite_weight(text, ',', '(', ')').unwrap();
assert_eq!(parts, vec!["1.0", "2.0", "3.0"]);
let wrapped_text = "(1.0, 2.0)";
let parts2 = split_composite_weight(wrapped_text, ',', '(', ')').unwrap();
assert_eq!(parts2, vec!["1.0", "2.0"]);
let nested_text = "(1.0, 2.0), (3.0, 4.0), 5.0";
let nested_parts = split_composite_weight(nested_text, ',', '(', ')').unwrap();
assert_eq!(nested_parts, vec!["(1.0, 2.0)", "(3.0, 4.0)", "5.0"]);
let error_text = "(1.0, 2.0";
assert_eq!(
split_composite_weight(error_text, ',', '(', ')'),
Err(SplitCompositeError::UnmatchedOpenParenthesis)
);
let error_text2 = "1.0, 2.0)";
assert_eq!(
split_composite_weight(error_text2, ',', '(', ')'),
Err(SplitCompositeError::UnmatchedCloseParenthesis { offset: 8 })
);
}
}