Skip to content

Commit 2a18d58

Browse files
authored
Provide more information when a required item is missing (#293)
1 parent 7de45f9 commit 2a18d58

3 files changed

Lines changed: 316 additions & 5 deletions

File tree

crates/quarto-yaml-validation/src/diagnostic.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ impl ValidationDiagnostic {
253253
let mut hints = Vec::new();
254254

255255
match kind {
256-
ValidationErrorKind::MissingRequiredProperty { property } => {
256+
ValidationErrorKind::MissingRequiredProperty { property, .. } => {
257257
hints.push(format!(
258258
"Add the `{}` property to your YAML document?",
259259
property
@@ -355,6 +355,8 @@ mod tests {
355355
let error = ValidationError::new(
356356
ValidationErrorKind::MissingRequiredProperty {
357357
property: "author".to_string(),
358+
allowed: None,
359+
expected_type: None,
358360
},
359361
InstancePath::new(),
360362
);
@@ -393,6 +395,8 @@ mod tests {
393395

394396
let kind = ValidationErrorKind::MissingRequiredProperty {
395397
property: "author".to_string(),
398+
allowed: None,
399+
expected_type: None,
396400
};
397401
let hints = ValidationDiagnostic::suggest_fixes_from_kind(&kind);
398402
assert_eq!(hints.len(), 1);

crates/quarto-yaml-validation/src/error.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,19 @@ pub enum ValidationErrorKind {
8787
TypeMismatch { expected: String, got: String },
8888

8989
/// Missing required property
90-
MissingRequiredProperty { property: String },
90+
///
91+
/// When the schema knows what the absent property should have been, the
92+
/// error advertises it, mirroring the message the user would have seen had
93+
/// the property been present with a bad value:
94+
/// - `allowed` carries the permitted values when the subschema is an enum
95+
/// (like [`ValidationErrorKind::InvalidEnumValue`]).
96+
/// - `expected_type` carries the permitted type(s) otherwise (like
97+
/// [`ValidationErrorKind::TypeMismatch`]).
98+
MissingRequiredProperty {
99+
property: String,
100+
allowed: Option<Vec<String>>,
101+
expected_type: Option<String>,
102+
},
91103

92104
/// Unknown property in closed object
93105
UnknownProperty { property: String },
@@ -176,9 +188,23 @@ impl ValidationErrorKind {
176188
ValidationErrorKind::TypeMismatch { expected, got } => {
177189
format!("Expected {}, got {}", expected, got)
178190
}
179-
ValidationErrorKind::MissingRequiredProperty { property } => {
180-
format!("Missing required property '{}'", property)
181-
}
191+
ValidationErrorKind::MissingRequiredProperty {
192+
property,
193+
allowed,
194+
expected_type,
195+
} => match (allowed, expected_type) {
196+
(Some(values), _) if !values.is_empty() => {
197+
format!(
198+
"Missing required property '{}' (must be one of: {})",
199+
property,
200+
values.join(", ")
201+
)
202+
}
203+
(_, Some(ty)) => {
204+
format!("Missing required property '{}' (expected {})", property, ty)
205+
}
206+
_ => format!("Missing required property '{}'", property),
207+
},
182208
ValidationErrorKind::UnknownProperty { property } => {
183209
format!("Unknown property '{}'", property)
184210
}
@@ -641,6 +667,8 @@ mod tests {
641667
fn test_error_code_missing_required_property() {
642668
let kind = ValidationErrorKind::MissingRequiredProperty {
643669
property: "foo".to_string(),
670+
allowed: None,
671+
expected_type: None,
644672
};
645673
assert_eq!(kind.error_code(), "Q-1-10");
646674
}
@@ -994,6 +1022,8 @@ mod tests {
9941022
let error = ValidationError::new(
9951023
ValidationErrorKind::MissingRequiredProperty {
9961024
property: "toc".to_string(),
1025+
allowed: None,
1026+
expected_type: None,
9971027
},
9981028
path,
9991029
);
@@ -1003,6 +1033,30 @@ mod tests {
10031033
);
10041034
}
10051035

1036+
#[test]
1037+
fn test_missing_required_property_with_allowed_values() {
1038+
let kind = ValidationErrorKind::MissingRequiredProperty {
1039+
property: "version".to_string(),
1040+
allowed: Some(vec!["0.1.0".to_string()]),
1041+
expected_type: None,
1042+
};
1043+
assert_eq!(
1044+
kind.message(),
1045+
"Missing required property 'version' (must be one of: 0.1.0)"
1046+
);
1047+
}
1048+
1049+
#[test]
1050+
fn test_missing_required_property_empty_allowed_values() {
1051+
// An empty allowed list must not append a "(must be one of: )" clause.
1052+
let kind = ValidationErrorKind::MissingRequiredProperty {
1053+
property: "version".to_string(),
1054+
allowed: Some(vec![]),
1055+
expected_type: None,
1056+
};
1057+
assert_eq!(kind.message(), "Missing required property 'version'");
1058+
}
1059+
10061060
// Tests for ValidationError::with_schema_path
10071061
#[test]
10081062
fn test_validation_error_with_schema_path() {

0 commit comments

Comments
 (0)