Fix multi-line strings are not allowed for key
In spec https://github.com/toml-lang/toml#keys Quoted keys are clarified as > he exact same rules as either basic strings or literal strings TOML clearly distinguishes basic string and multi-line basic string (literal string is also). https://github.com/toml-lang/toml#string So table key and quoted key should not allow multi-line basic string and multi-line literal string. ABNF definition also describes that. https://github.com/toml-lang/toml/blob/master/toml.abnf ``` string = ml-basic-string / basic-string / ml-literal-string / literal-string quoted-key = basic-string / literal-string ``` `string` contains `ml-*` but `quoted-key` doesn't.
This commit is contained in:
parent
48daf82e1a
commit
935d95ce4f
|
@ -152,6 +152,9 @@ enum ErrorKind {
|
||||||
/// An empty table key was found.
|
/// An empty table key was found.
|
||||||
EmptyTableKey,
|
EmptyTableKey,
|
||||||
|
|
||||||
|
/// Multiline strings are not allowed for key
|
||||||
|
MultilineStringKey,
|
||||||
|
|
||||||
/// A custom error which could be generated when deserializing a particular
|
/// A custom error which could be generated when deserializing a particular
|
||||||
/// type.
|
/// type.
|
||||||
Custom,
|
Custom,
|
||||||
|
@ -1274,6 +1277,9 @@ impl<'a> Deserializer<'a> {
|
||||||
TokenError::EmptyTableKey(at) => {
|
TokenError::EmptyTableKey(at) => {
|
||||||
self.error(at, ErrorKind::EmptyTableKey)
|
self.error(at, ErrorKind::EmptyTableKey)
|
||||||
}
|
}
|
||||||
|
TokenError::MultilineStringKey(at) => {
|
||||||
|
self.error(at, ErrorKind::MultilineStringKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,6 +1383,7 @@ impl fmt::Display for Error {
|
||||||
}
|
}
|
||||||
ErrorKind::RedefineAsArray => "table redefined as array".fmt(f)?,
|
ErrorKind::RedefineAsArray => "table redefined as array".fmt(f)?,
|
||||||
ErrorKind::EmptyTableKey => "empty table key found".fmt(f)?,
|
ErrorKind::EmptyTableKey => "empty table key found".fmt(f)?,
|
||||||
|
ErrorKind::MultilineStringKey => "multiline strings are not allowed for key".fmt(f)?,
|
||||||
ErrorKind::Custom => self.inner.message.fmt(f)?,
|
ErrorKind::Custom => self.inner.message.fmt(f)?,
|
||||||
ErrorKind::ExpectedString => "expected string".fmt(f)?,
|
ErrorKind::ExpectedString => "expected string".fmt(f)?,
|
||||||
ErrorKind::DottedKeyInvalidType => "dotted key attempted to extend non-table type".fmt(f)?,
|
ErrorKind::DottedKeyInvalidType => "dotted key attempted to extend non-table type".fmt(f)?,
|
||||||
|
@ -1421,6 +1428,7 @@ impl error::Error for Error {
|
||||||
ErrorKind::DuplicateTable(_) => "duplicate table",
|
ErrorKind::DuplicateTable(_) => "duplicate table",
|
||||||
ErrorKind::RedefineAsArray => "table redefined as array",
|
ErrorKind::RedefineAsArray => "table redefined as array",
|
||||||
ErrorKind::EmptyTableKey => "empty table key found",
|
ErrorKind::EmptyTableKey => "empty table key found",
|
||||||
|
ErrorKind::MultilineStringKey => "invalid multiline string for key",
|
||||||
ErrorKind::Custom => "a custom error",
|
ErrorKind::Custom => "a custom error",
|
||||||
ErrorKind::ExpectedString => "expected string",
|
ErrorKind::ExpectedString => "expected string",
|
||||||
ErrorKind::DottedKeyInvalidType => "dotted key invalid type",
|
ErrorKind::DottedKeyInvalidType => "dotted key invalid type",
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub enum Token<'a> {
|
||||||
RightBracket,
|
RightBracket,
|
||||||
|
|
||||||
Keylike(&'a str),
|
Keylike(&'a str),
|
||||||
String { src: &'a str, val: Cow<'a, str> },
|
String { src: &'a str, val: Cow<'a, str>, multiline: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
|
@ -51,6 +51,7 @@ pub enum Error {
|
||||||
Unexpected(usize, char),
|
Unexpected(usize, char),
|
||||||
UnterminatedString(usize),
|
UnterminatedString(usize),
|
||||||
NewlineInTableKey(usize),
|
NewlineInTableKey(usize),
|
||||||
|
MultilineStringKey(usize),
|
||||||
EmptyTableKey(usize),
|
EmptyTableKey(usize),
|
||||||
Wanted { at: usize, expected: &'static str, found: &'static str },
|
Wanted { at: usize, expected: &'static str, found: &'static str },
|
||||||
}
|
}
|
||||||
|
@ -169,8 +170,11 @@ impl<'a> Tokenizer<'a> {
|
||||||
let current = self.current();
|
let current = self.current();
|
||||||
match self.next()? {
|
match self.next()? {
|
||||||
Some((span, Token::Keylike(k))) => Ok((span, k.into())),
|
Some((span, Token::Keylike(k))) => Ok((span, k.into())),
|
||||||
Some((span, Token::String { src, val })) => {
|
Some((span, Token::String { src, val, multiline })) => {
|
||||||
let offset = self.substr_offset(src);
|
let offset = self.substr_offset(src);
|
||||||
|
if multiline {
|
||||||
|
return Err(Error::MultilineStringKey(offset))
|
||||||
|
}
|
||||||
if val == "" {
|
if val == "" {
|
||||||
return Err(Error::EmptyTableKey(offset))
|
return Err(Error::EmptyTableKey(offset))
|
||||||
}
|
}
|
||||||
|
@ -286,6 +290,7 @@ impl<'a> Tokenizer<'a> {
|
||||||
return Ok(String {
|
return Ok(String {
|
||||||
src: &self.input[start..start+2],
|
src: &self.input[start..start+2],
|
||||||
val: Cow::Borrowed(""),
|
val: Cow::Borrowed(""),
|
||||||
|
multiline: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,6 +326,7 @@ impl<'a> Tokenizer<'a> {
|
||||||
return Ok(String {
|
return Ok(String {
|
||||||
src: &self.input[start..self.current()],
|
src: &self.input[start..self.current()],
|
||||||
val: val.into_cow(&self.input[..i]),
|
val: val.into_cow(&self.input[..i]),
|
||||||
|
multiline: multiline,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some((i, c)) => new_ch(self, &mut val, multiline, i, c)?,
|
Some((i, c)) => new_ch(self, &mut val, multiline, i, c)?,
|
||||||
|
@ -497,7 +503,7 @@ impl<'a> Token<'a> {
|
||||||
Token::LeftBrace => "a left brace",
|
Token::LeftBrace => "a left brace",
|
||||||
Token::RightBracket => "a right bracket",
|
Token::RightBracket => "a right bracket",
|
||||||
Token::LeftBracket => "a left bracket",
|
Token::LeftBracket => "a left bracket",
|
||||||
Token::String { .. } => "a string",
|
Token::String { multiline, .. } => if multiline { "a multiline string" } else { "a string" },
|
||||||
Token::Colon => "a colon",
|
Token::Colon => "a colon",
|
||||||
Token::Plus => "a plus",
|
Token::Plus => "a plus",
|
||||||
}
|
}
|
||||||
|
@ -518,56 +524,58 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn literal_strings() {
|
fn literal_strings() {
|
||||||
fn t(input: &str, val: &str) {
|
fn t(input: &str, val: &str, multiline: bool) {
|
||||||
let mut t = Tokenizer::new(input);
|
let mut t = Tokenizer::new(input);
|
||||||
let (_, token) = t.next().unwrap().unwrap();
|
let (_, token) = t.next().unwrap().unwrap();
|
||||||
assert_eq!(token, Token::String {
|
assert_eq!(token, Token::String {
|
||||||
src: input,
|
src: input,
|
||||||
val: Cow::Borrowed(val),
|
val: Cow::Borrowed(val),
|
||||||
|
multiline: multiline,
|
||||||
});
|
});
|
||||||
assert!(t.next().unwrap().is_none());
|
assert!(t.next().unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
t("''", "");
|
t("''", "", false);
|
||||||
t("''''''", "");
|
t("''''''", "", true);
|
||||||
t("'''\n'''", "");
|
t("'''\n'''", "", true);
|
||||||
t("'a'", "a");
|
t("'a'", "a", false);
|
||||||
t("'\"a'", "\"a");
|
t("'\"a'", "\"a", false);
|
||||||
t("''''a'''", "'a");
|
t("''''a'''", "'a", true);
|
||||||
t("'''\n'a\n'''", "'a\n");
|
t("'''\n'a\n'''", "'a\n", true);
|
||||||
t("'''a\n'a\r\n'''", "a\n'a\n");
|
t("'''a\n'a\r\n'''", "a\n'a\n", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_strings() {
|
fn basic_strings() {
|
||||||
fn t(input: &str, val: &str) {
|
fn t(input: &str, val: &str, multiline: bool) {
|
||||||
let mut t = Tokenizer::new(input);
|
let mut t = Tokenizer::new(input);
|
||||||
let (_, token) = t.next().unwrap().unwrap();
|
let (_, token) = t.next().unwrap().unwrap();
|
||||||
assert_eq!(token, Token::String {
|
assert_eq!(token, Token::String {
|
||||||
src: input,
|
src: input,
|
||||||
val: Cow::Borrowed(val),
|
val: Cow::Borrowed(val),
|
||||||
|
multiline: multiline,
|
||||||
});
|
});
|
||||||
assert!(t.next().unwrap().is_none());
|
assert!(t.next().unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
t(r#""""#, "");
|
t(r#""""#, "", false);
|
||||||
t(r#""""""""#, "");
|
t(r#""""""""#, "", true);
|
||||||
t(r#""a""#, "a");
|
t(r#""a""#, "a", false);
|
||||||
t(r#""""a""""#, "a");
|
t(r#""""a""""#, "a", true);
|
||||||
t(r#""\t""#, "\t");
|
t(r#""\t""#, "\t", false);
|
||||||
t(r#""\u0000""#, "\0");
|
t(r#""\u0000""#, "\0", false);
|
||||||
t(r#""\U00000000""#, "\0");
|
t(r#""\U00000000""#, "\0", false);
|
||||||
t(r#""\U000A0000""#, "\u{A0000}");
|
t(r#""\U000A0000""#, "\u{A0000}", false);
|
||||||
t(r#""\\t""#, "\\t");
|
t(r#""\\t""#, "\\t", false);
|
||||||
t("\"\"\"\\\n\"\"\"", "");
|
t("\"\"\"\\\n\"\"\"", "", true);
|
||||||
t("\"\"\"\\\n \t \t \\\r\n \t \n \t \r\n\"\"\"", "");
|
t("\"\"\"\\\n \t \t \\\r\n \t \n \t \r\n\"\"\"", "", true);
|
||||||
t(r#""\r""#, "\r");
|
t(r#""\r""#, "\r", false);
|
||||||
t(r#""\n""#, "\n");
|
t(r#""\n""#, "\n", false);
|
||||||
t(r#""\b""#, "\u{8}");
|
t(r#""\b""#, "\u{8}", false);
|
||||||
t(r#""a\fa""#, "a\u{c}a");
|
t(r#""a\fa""#, "a\u{c}a", false);
|
||||||
t(r#""\"a""#, "\"a");
|
t(r#""\"a""#, "\"a", false);
|
||||||
t("\"\"\"\na\"\"\"", "a");
|
t("\"\"\"\na\"\"\"", "a", true);
|
||||||
t("\"\"\"\n\"\"\"", "");
|
t("\"\"\"\n\"\"\"", "", true);
|
||||||
err(r#""\a"#, Error::InvalidEscape(2, 'a'));
|
err(r#""\a"#, Error::InvalidEscape(2, 'a'));
|
||||||
err("\"\\\n", Error::InvalidEscape(2, '\n'));
|
err("\"\\\n", Error::InvalidEscape(2, '\n'));
|
||||||
err("\"\\\r\n", Error::InvalidEscape(2, '\n'));
|
err("\"\\\r\n", Error::InvalidEscape(2, '\n'));
|
||||||
|
|
|
@ -275,6 +275,10 @@ fn bad_keys() {
|
||||||
"\"\"|=3".parse::<Value>().unwrap_err();
|
"\"\"|=3".parse::<Value>().unwrap_err();
|
||||||
"\"\n\"|=3".parse::<Value>().unwrap_err();
|
"\"\n\"|=3".parse::<Value>().unwrap_err();
|
||||||
"\"\r\"|=3".parse::<Value>().unwrap_err();
|
"\"\r\"|=3".parse::<Value>().unwrap_err();
|
||||||
|
"''''''=3".parse::<Value>().unwrap_err();
|
||||||
|
"\"\"\"\"\"\"=3".parse::<Value>().unwrap_err();
|
||||||
|
"'''key'''=3".parse::<Value>().unwrap_err();
|
||||||
|
"\"\"\"key\"\"\"=3".parse::<Value>().unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -290,6 +294,8 @@ fn bad_table_names() {
|
||||||
"[']".parse::<Value>().unwrap_err();
|
"[']".parse::<Value>().unwrap_err();
|
||||||
"[''']".parse::<Value>().unwrap_err();
|
"[''']".parse::<Value>().unwrap_err();
|
||||||
"['''''']".parse::<Value>().unwrap_err();
|
"['''''']".parse::<Value>().unwrap_err();
|
||||||
|
"['''foo''']".parse::<Value>().unwrap_err();
|
||||||
|
"[\"\"\"bar\"\"\"]".parse::<Value>().unwrap_err();
|
||||||
"['\n']".parse::<Value>().unwrap_err();
|
"['\n']".parse::<Value>().unwrap_err();
|
||||||
"['\r\n']".parse::<Value>().unwrap_err();
|
"['\r\n']".parse::<Value>().unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue