Skip to content

Commit 5e9d985

Browse files
Fix inconsistencies with cpython while parsing format strings (#124)
* Fix inconsistencies with cpython while parsing format strings which contain colons inside square brackets Co-authored-by: Jeong, YunWon <[email protected]>
1 parent a5b0809 commit 5e9d985

File tree

1 file changed

+65
-8
lines changed

1 file changed

+65
-8
lines changed

format/src/format.rs

+65-8
Original file line numberDiff line numberDiff line change
@@ -863,17 +863,46 @@ impl FormatString {
863863
}
864864

865865
fn parse_part_in_brackets(text: &str) -> Result<FormatPart, FormatParseError> {
866-
let parts: Vec<&str> = text.splitn(2, ':').collect();
866+
let mut chars = text.chars().peekable();
867+
868+
let mut left = String::new();
869+
let mut right = String::new();
870+
871+
let mut split = false;
872+
let mut selected = &mut left;
873+
let mut inside_brackets = false;
874+
875+
while let Some(char) = chars.next() {
876+
if char == '[' {
877+
inside_brackets = true;
878+
879+
selected.push(char);
880+
881+
while let Some(next_char) = chars.next() {
882+
selected.push(next_char);
883+
884+
if next_char == ']' {
885+
inside_brackets = false;
886+
break;
887+
}
888+
if chars.peek().is_none() {
889+
return Err(FormatParseError::MissingRightBracket);
890+
}
891+
}
892+
} else if char == ':' && !split && !inside_brackets {
893+
split = true;
894+
selected = &mut right;
895+
} else {
896+
selected.push(char);
897+
}
898+
}
899+
867900
// before the comma is a keyword or arg index, after the comma is maybe a spec.
868-
let arg_part = parts[0];
901+
let arg_part: &str = &left;
869902

870-
let format_spec = if parts.len() > 1 {
871-
parts[1].to_owned()
872-
} else {
873-
String::new()
874-
};
903+
let format_spec = if split { right } else { String::new() };
875904

876-
// On parts[0] can still be the conversion (!r, !s, !a)
905+
// left can still be the conversion (!r, !s, !a)
877906
let parts: Vec<&str> = arg_part.splitn(2, '!').collect();
878907
// before the bang is a keyword or arg index, after the comma is maybe a conversion spec.
879908
let arg_part = parts[0];
@@ -1168,6 +1197,34 @@ mod tests {
11681197
);
11691198
}
11701199

1200+
#[test]
1201+
fn test_square_brackets_inside_format() {
1202+
assert_eq!(
1203+
FormatString::from_str("{[:123]}"),
1204+
Ok(FormatString {
1205+
format_parts: vec![FormatPart::Field {
1206+
field_name: "[:123]".to_owned(),
1207+
conversion_spec: None,
1208+
format_spec: "".to_owned(),
1209+
}],
1210+
}),
1211+
);
1212+
1213+
assert_eq!(FormatString::from_str("{asdf[:123]asdf}"), {
1214+
Ok(FormatString {
1215+
format_parts: vec![FormatPart::Field {
1216+
field_name: "asdf[:123]asdf".to_owned(),
1217+
conversion_spec: None,
1218+
format_spec: "".to_owned(),
1219+
}],
1220+
})
1221+
});
1222+
1223+
assert_eq!(FormatString::from_str("{[1234}"), {
1224+
Err(FormatParseError::MissingRightBracket)
1225+
});
1226+
}
1227+
11711228
#[test]
11721229
fn test_format_parse_escape() {
11731230
let expected = Ok(FormatString {

0 commit comments

Comments
 (0)