fix: better errors on toml parsing failures

This commit is contained in:
Tom Milligan
2024-05-24 09:44:47 +01:00
parent 294af2478c
commit 2c18292401
3 changed files with 70 additions and 44 deletions

View File

@@ -39,7 +39,9 @@
</div>
<div>
<p>Failed with:</p>
<pre><code class="language-log">TOML parsing error: TOML parse error at line 1, column 8
<pre><code class="language-log">'title=&quot;' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 8
|
1 | title=&quot;
| ^

View File

@@ -21,10 +21,8 @@ struct UserInput {
fn bare_key_value_pairs_to_toml(pairs: &str) -> String {
use regex::Captures;
static RX_BARE_KEY_ASSIGNMENT: Lazy<Regex> = Lazy::new(|| {
let bare_key = r#"[A-Za-z0-9_-]+"#;
Regex::new(&format!("(?:{bare_key}) *=")).expect("bare key assignment regex")
});
static RX_BARE_KEY_ASSIGNMENT: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"(?:[A-Za-z0-9_-]+) *="#).expect("bare key assignment regex"));
fn prefix_with_newline(captures: &Captures) -> String {
format!(
@@ -52,7 +50,7 @@ pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig,
let config: UserInput = match toml::from_str(config_toml) {
Ok(config) => config,
Err(error) => {
let original_error = Err(format!("TOML parsing error: {error}"));
let original_error = format!("TOML parsing error: {error}");
// For ergonomic reasons, we allow users to specify the directive without
// a key. So if parsing fails initially, take the first line,
@@ -66,12 +64,14 @@ pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig,
Lazy::new(|| Regex::new(r#"^[A-Za-z0-9_-]+$"#).expect("directive regex"));
if !RX_DIRECTIVE.is_match(directive) {
return original_error;
return Err(format!("'{directive}' is not a valid directive or TOML key-value pair.\n\n{original_error}"));
}
let mut config: UserInput = match toml::from_str(config_toml) {
Ok(config) => config,
Err(_) => return original_error,
Err(error) => {
return Err(format!("TOML parsing error: {error}"));
}
};
config.r#type = Some(directive.to_owned());
config
@@ -102,97 +102,119 @@ mod test {
use pretty_assertions::assert_eq;
#[test]
fn test_from_config_string_v2() {
assert_eq!(
from_config_string("").unwrap(),
fn test_from_config_string_v2() -> Result<(), ()> {
fn check(config_string: &str, expected: InstanceConfig) -> Result<(), ()> {
let actual = match from_config_string(config_string) {
Ok(config) => config,
Err(error) => panic!("Expected config to be valid, got error:\n\n{}", error),
};
assert_eq!(actual, expected);
Ok(())
}
check(
"",
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
assert_eq!(
from_config_string(" ").unwrap(),
},
)?;
check(
" ",
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
assert_eq!(
from_config_string(
r#"type="note" class="additional classname" title="Никита" collapsible=true"#
)
.unwrap(),
},
)?;
check(
r#"type="note" class="additional classname" title="Никита" collapsible=true"#,
InstanceConfig {
directive: "note".to_owned(),
title: Some("Никита".to_owned()),
id: None,
additional_classnames: vec!["additional".to_owned(), "classname".to_owned()],
collapsible: Some(true),
}
);
},
)?;
// Specifying unknown keys is okay, as long as they're valid
assert_eq!(
from_config_string(r#"unkonwn="but valid toml""#).unwrap(),
check(
r#"unkonwn="but valid toml""#,
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
},
)?;
// Just directive is fine
assert_eq!(
from_config_string(r#"info"#).unwrap(),
check(
r#"info"#,
InstanceConfig {
directive: "info".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
},
)?;
// Directive plus toml config
assert_eq!(
from_config_string(r#"info title="Information" collapsible=false"#).unwrap(),
check(
r#"info title="Information" collapsible=false"#,
InstanceConfig {
directive: "info".to_owned(),
title: Some("Information".to_owned()),
id: None,
additional_classnames: Vec::new(),
collapsible: Some(false),
}
);
},
)?;
// Test custom id
assert_eq!(
from_config_string(r#"info title="My Info" id="my-info-custom-id""#).unwrap(),
check(
r#"info title="My Info" id="my-info-custom-id""#,
InstanceConfig {
directive: "info".to_owned(),
title: Some("My Info".to_owned()),
id: Some("my-info-custom-id".to_owned()),
additional_classnames: Vec::new(),
collapsible: None,
}
);
},
)?;
// Directive after toml config is an error
assert!(from_config_string(r#"title="Information" info"#).is_err());
Ok(())
}
#[test]
fn test_from_config_string_invalid_directive() {
assert_eq!(
from_config_string(r#"oh!wow titlel=""#).unwrap_err(),
r#"'oh!wow' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 3
|
1 | oh!wow
| ^
expected `.`, `=`
"#
);
}
#[test]
fn test_from_config_string_invalid_toml_value() {
assert_eq!(
from_config_string(r#"note titlel=""#).unwrap_err(),
r#"TOML parsing error: TOML parse error at line 1, column 6
r#"TOML parsing error: TOML parse error at line 1, column 9
|
1 | note
| ^
expected `.`, `=`
1 | titlel="
| ^
invalid basic string
"#
);
}

View File

@@ -596,6 +596,8 @@ Error rendering admonishment
Failed with:
```log
'title="' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 8
|
1 | title="