Skip to content

Commit 7d39049

Browse files
committed
fix: render recipe even when there're frontmatter YAML syntax issues
1 parent 2eb9dc0 commit 7d39049

2 files changed

Lines changed: 143 additions & 2 deletions

File tree

src/analysis/event_consumer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,15 @@ impl<'i> RecipeCollector<'i, '_> {
228228
Err(err) => {
229229
// ! This message (can) contains line and column number, but line numbers
230230
// ! are off by one thanks to the starting `---`
231-
let mut diag = error!(err.to_string());
231+
let mut diag = warning!(format!("Invalid YAML frontmatter syntax: {}", err));
232232
let err_span = err
233233
.location()
234234
.map(|loc| Span::pos(yaml_text.span().start() + loc.index()));
235235
if let Some(loc) = err_span {
236236
diag = diag.label(label!(loc));
237237
}
238-
self.ctx.error(diag);
238+
diag.add_hint("The frontmatter will be ignored. Fix the YAML syntax to use metadata.");
239+
self.ctx.warn(diag);
239240
return;
240241
}
241242
};

tests/frontmatter_tests.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use cooklang;
2+
use indoc::indoc;
3+
4+
#[test]
5+
fn test_invalid_yaml_frontmatter_becomes_warning() {
6+
let input = indoc! {r#"
7+
---
8+
title: Test Recipe
9+
tags: [test
10+
invalid yaml here
11+
---
12+
13+
This is a test recipe with invalid YAML frontmatter.
14+
15+
@eggs{2} and @butter{1%tbsp}
16+
"#};
17+
18+
let result = cooklang::parse(input);
19+
20+
// Should not fail to parse
21+
assert!(result.output().is_some(), "Recipe should parse successfully despite invalid YAML");
22+
23+
let recipe = result.output().unwrap();
24+
let warnings = result.report();
25+
26+
// Should have warnings about invalid YAML
27+
assert!(!warnings.is_empty(), "Should have warnings about invalid YAML");
28+
29+
// The frontmatter should be ignored, so metadata should be empty
30+
assert!(recipe.metadata.map.is_empty(), "Metadata should be empty when YAML is invalid");
31+
32+
// Should still parse the recipe content
33+
assert_eq!(recipe.ingredients.len(), 2, "Should still parse ingredients");
34+
assert_eq!(recipe.ingredients[0].name, "eggs");
35+
assert_eq!(recipe.ingredients[1].name, "butter");
36+
}
37+
38+
#[test]
39+
fn test_valid_yaml_frontmatter_still_works() {
40+
let input = indoc! {r#"
41+
---
42+
title: Test Recipe
43+
tags: [test, recipe]
44+
prep_time: 10 min
45+
---
46+
47+
This is a test recipe with valid YAML frontmatter.
48+
49+
@eggs{2} and @butter{1%tbsp}
50+
"#};
51+
52+
let result = cooklang::parse(input);
53+
54+
// Should parse successfully
55+
assert!(result.output().is_some(), "Recipe should parse successfully");
56+
57+
let recipe = result.output().unwrap();
58+
59+
// Metadata should be parsed
60+
assert!(!recipe.metadata.map.is_empty(), "Metadata should not be empty");
61+
assert_eq!(
62+
recipe.metadata.map.get("title").and_then(|v| v.as_str()),
63+
Some("Test Recipe"),
64+
"Title should be parsed correctly"
65+
);
66+
67+
// Should still parse the recipe content
68+
assert_eq!(recipe.ingredients.len(), 2, "Should parse ingredients");
69+
assert_eq!(recipe.ingredients[0].name, "eggs");
70+
assert_eq!(recipe.ingredients[1].name, "butter");
71+
}
72+
73+
#[test]
74+
fn test_invalid_yaml_with_colon_in_value() {
75+
let input = indoc! {r#"
76+
---
77+
title: Recipe: with colon
78+
description: This has: many: colons
79+
tags: [unclosed
80+
---
81+
82+
@flour{2%cups}
83+
"#};
84+
85+
let result = cooklang::parse(input);
86+
87+
// Should not fail to parse
88+
assert!(result.output().is_some(), "Recipe should parse successfully despite invalid YAML");
89+
90+
let recipe = result.output().unwrap();
91+
let warnings = result.report();
92+
93+
// Should have warnings
94+
assert!(!warnings.is_empty(), "Should have warnings about invalid YAML");
95+
96+
// Metadata should be empty due to invalid YAML
97+
assert!(recipe.metadata.map.is_empty(), "Metadata should be empty when YAML is invalid");
98+
99+
// Should still parse ingredients
100+
assert_eq!(recipe.ingredients.len(), 1);
101+
assert_eq!(recipe.ingredients[0].name, "flour");
102+
}
103+
104+
#[test]
105+
fn test_completely_malformed_yaml() {
106+
let input = indoc! {r#"
107+
---
108+
{ this is not valid yaml at all }}}
109+
: : : :
110+
---
111+
112+
Simple recipe with @salt and @pepper
113+
"#};
114+
115+
let result = cooklang::parse(input);
116+
117+
// Should not fail to parse
118+
assert!(result.output().is_some(), "Recipe should parse successfully despite malformed YAML");
119+
120+
let recipe = result.output().unwrap();
121+
let warnings = result.report();
122+
123+
// Should have warnings
124+
assert!(!warnings.is_empty(), "Should have warnings about invalid YAML");
125+
126+
// Should contain information about invalid YAML in warning
127+
let warning_str = format!("{:?}", warnings);
128+
assert!(
129+
warning_str.contains("Invalid YAML") || warning_str.contains("invalid YAML"),
130+
"Warning should mention invalid YAML"
131+
);
132+
133+
// Metadata should be empty
134+
assert!(recipe.metadata.map.is_empty(), "Metadata should be empty when YAML is invalid");
135+
136+
// Should still parse the recipe
137+
assert_eq!(recipe.ingredients.len(), 2);
138+
assert_eq!(recipe.ingredients[0].name, "salt");
139+
assert_eq!(recipe.ingredients[1].name, "pepper");
140+
}

0 commit comments

Comments
 (0)