Skip to content

Commit eda8875

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

File tree

1 file changed

+75
-8
lines changed

1 file changed

+75
-8
lines changed

format/src/format.rs

+75-8
Original file line numberDiff line numberDiff line change
@@ -863,17 +863,56 @@ 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() == None {
896+
return Err(FormatParseError::MissingRightBracket);
897+
}
898+
}
899+
} else if char == ':' && split == false && inside_brackets == false {
900+
split = true;
901+
} else {
902+
if split {
903+
right.push(char);
904+
} else {
905+
left.push(char);
906+
}
907+
}
908+
}
909+
867910
// before the comma is a keyword or arg index, after the comma is maybe a spec.
868-
let arg_part = parts[0];
911+
let arg_part: &str = &left;
869912

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

876-
// On parts[0] can still be the conversion (!r, !s, !a)
915+
// left can still be the conversion (!r, !s, !a)
877916
let parts: Vec<&str> = arg_part.splitn(2, '!').collect();
878917
// before the bang is a keyword or arg index, after the comma is maybe a conversion spec.
879918
let arg_part = parts[0];
@@ -1168,6 +1207,34 @@ mod tests {
11681207
);
11691208
}
11701209

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

0 commit comments

Comments
 (0)