diff --git a/src/analysis/event_consumer.rs b/src/analysis/event_consumer.rs index 448e49f..f71cedb 100644 --- a/src/analysis/event_consumer.rs +++ b/src/analysis/event_consumer.rs @@ -228,14 +228,15 @@ impl<'i> RecipeCollector<'i, '_> { Err(err) => { // ! This message (can) contains line and column number, but line numbers // ! are off by one thanks to the starting `---` - let mut diag = error!(err.to_string()); + let mut diag = warning!(format!("Invalid YAML frontmatter syntax: {}", err)); let err_span = err .location() .map(|loc| Span::pos(yaml_text.span().start() + loc.index())); if let Some(loc) = err_span { diag = diag.label(label!(loc)); } - self.ctx.error(diag); + diag.add_hint("The frontmatter will be ignored. Fix the YAML syntax to use metadata."); + self.ctx.warn(diag); return; } }; diff --git a/tests/frontmatter_tests.rs b/tests/frontmatter_tests.rs new file mode 100644 index 0000000..e96b352 --- /dev/null +++ b/tests/frontmatter_tests.rs @@ -0,0 +1,140 @@ +use cooklang; +use indoc::indoc; + +#[test] +fn test_invalid_yaml_frontmatter_becomes_warning() { + let input = indoc! {r#" + --- + title: Test Recipe + tags: [test + invalid yaml here + --- + + This is a test recipe with invalid YAML frontmatter. + + @eggs{2} and @butter{1%tbsp} + "#}; + + let result = cooklang::parse(input); + + // Should not fail to parse + assert!(result.output().is_some(), "Recipe should parse successfully despite invalid YAML"); + + let recipe = result.output().unwrap(); + let warnings = result.report(); + + // Should have warnings about invalid YAML + assert!(!warnings.is_empty(), "Should have warnings about invalid YAML"); + + // The frontmatter should be ignored, so metadata should be empty + assert!(recipe.metadata.map.is_empty(), "Metadata should be empty when YAML is invalid"); + + // Should still parse the recipe content + assert_eq!(recipe.ingredients.len(), 2, "Should still parse ingredients"); + assert_eq!(recipe.ingredients[0].name, "eggs"); + assert_eq!(recipe.ingredients[1].name, "butter"); +} + +#[test] +fn test_valid_yaml_frontmatter_still_works() { + let input = indoc! {r#" + --- + title: Test Recipe + tags: [test, recipe] + prep_time: 10 min + --- + + This is a test recipe with valid YAML frontmatter. + + @eggs{2} and @butter{1%tbsp} + "#}; + + let result = cooklang::parse(input); + + // Should parse successfully + assert!(result.output().is_some(), "Recipe should parse successfully"); + + let recipe = result.output().unwrap(); + + // Metadata should be parsed + assert!(!recipe.metadata.map.is_empty(), "Metadata should not be empty"); + assert_eq!( + recipe.metadata.map.get("title").and_then(|v| v.as_str()), + Some("Test Recipe"), + "Title should be parsed correctly" + ); + + // Should still parse the recipe content + assert_eq!(recipe.ingredients.len(), 2, "Should parse ingredients"); + assert_eq!(recipe.ingredients[0].name, "eggs"); + assert_eq!(recipe.ingredients[1].name, "butter"); +} + +#[test] +fn test_invalid_yaml_with_colon_in_value() { + let input = indoc! {r#" + --- + title: Recipe: with colon + description: This has: many: colons + tags: [unclosed + --- + + @flour{2%cups} + "#}; + + let result = cooklang::parse(input); + + // Should not fail to parse + assert!(result.output().is_some(), "Recipe should parse successfully despite invalid YAML"); + + let recipe = result.output().unwrap(); + let warnings = result.report(); + + // Should have warnings + assert!(!warnings.is_empty(), "Should have warnings about invalid YAML"); + + // Metadata should be empty due to invalid YAML + assert!(recipe.metadata.map.is_empty(), "Metadata should be empty when YAML is invalid"); + + // Should still parse ingredients + assert_eq!(recipe.ingredients.len(), 1); + assert_eq!(recipe.ingredients[0].name, "flour"); +} + +#[test] +fn test_completely_malformed_yaml() { + let input = indoc! {r#" + --- + { this is not valid yaml at all }}} + : : : : + --- + + Simple recipe with @salt and @pepper + "#}; + + let result = cooklang::parse(input); + + // Should not fail to parse + assert!(result.output().is_some(), "Recipe should parse successfully despite malformed YAML"); + + let recipe = result.output().unwrap(); + let warnings = result.report(); + + // Should have warnings + assert!(!warnings.is_empty(), "Should have warnings about invalid YAML"); + + // Should contain information about invalid YAML in warning + let warning_str = format!("{:?}", warnings); + assert!( + warning_str.contains("Invalid YAML") || warning_str.contains("invalid YAML"), + "Warning should mention invalid YAML" + ); + + // Metadata should be empty + assert!(recipe.metadata.map.is_empty(), "Metadata should be empty when YAML is invalid"); + + // Should still parse the recipe + assert_eq!(recipe.ingredients.len(), 2); + assert_eq!(recipe.ingredients[0].name, "salt"); + assert_eq!(recipe.ingredients[1].name, "pepper"); +}