Skip to content

Commit 11d7413

Browse files
committed
Fix inconsistencies with cpython while parsing format strings which contain colons inside square brackets
1 parent a5b0809 commit 11d7413

File tree

1 file changed

+73
-8
lines changed

1 file changed

+73
-8
lines changed

format/src/format.rs

+73-8
Original file line numberDiff line numberDiff line change
@@ -863,17 +863,54 @@ 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 inside_brackets = false;
873+
874+
while let Some(char) = chars.next() {
875+
if char == '[' {
876+
inside_brackets = true;
877+
878+
if split {
879+
right.push(char);
880+
} else {
881+
left.push(char);
882+
}
883+
884+
while let Some(next_char) = chars.next() {
885+
if split {
886+
right.push(next_char);
887+
} else {
888+
left.push(next_char);
889+
}
890+
891+
if next_char == ']' {
892+
inside_brackets = false;
893+
break;
894+
}
895+
if chars.peek().is_none() {
896+
return Err(FormatParseError::MissingRightBracket);
897+
}
898+
}
899+
} else if char == ':' && !split && !inside_brackets {
900+
split = true;
901+
} else if split {
902+
right.push(char);
903+
} else {
904+
left.push(char);
905+
}
906+
}
907+
867908
// before the comma is a keyword or arg index, after the comma is maybe a spec.
868-
let arg_part = parts[0];
909+
let arg_part: &str = &left;
869910

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

876-
// On parts[0] can still be the conversion (!r, !s, !a)
913+
// left can still be the conversion (!r, !s, !a)
877914
let parts: Vec<&str> = arg_part.splitn(2, '!').collect();
878915
// before the bang is a keyword or arg index, after the comma is maybe a conversion spec.
879916
let arg_part = parts[0];
@@ -1168,6 +1205,34 @@ mod tests {
11681205
);
11691206
}
11701207

1208+
#[test]
1209+
fn test_square_brackets_inside_format() {
1210+
assert_eq!(
1211+
FormatString::from_str("{[:123]}"),
1212+
Ok(FormatString {
1213+
format_parts: vec![FormatPart::Field {
1214+
field_name: "[:123]".to_owned(),
1215+
conversion_spec: None,
1216+
format_spec: "".to_owned(),
1217+
}],
1218+
}),
1219+
);
1220+
1221+
assert_eq!(FormatString::from_str("{asdf[:123]asdf}"), {
1222+
Ok(FormatString {
1223+
format_parts: vec![FormatPart::Field {
1224+
field_name: "asdf[:123]asdf".to_owned(),
1225+
conversion_spec: None,
1226+
format_spec: "".to_owned(),
1227+
}],
1228+
})
1229+
});
1230+
1231+
assert_eq!(FormatString::from_str("{[1234}"), {
1232+
Err(FormatParseError::MissingRightBracket)
1233+
});
1234+
}
1235+
11711236
#[test]
11721237
fn test_format_parse_escape() {
11731238
let expected = Ok(FormatString {

0 commit comments

Comments
 (0)