Skip to content

Commit 884d071

Browse files
committed
chore: Improved error message for enum
Signed-off-by: Dmitry Dygalo <[email protected]>
1 parent 13f4b97 commit 884d071

File tree

4 files changed

+57
-5
lines changed

4 files changed

+57
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Improved error message for `enum`.
8+
59
## [0.31.0] - 2025-07-28
610

711
### Added

crates/jsonschema-py/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Improved error message for `enum`.
8+
59
## [0.31.0] - 2025-07-28
610

711
### Added

crates/jsonschema/src/error.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,48 @@ fn write_unexpected_suffix(f: &mut Formatter<'_>, len: usize) -> fmt::Result {
803803
})
804804
}
805805

806+
const MAX_DISPLAYED_ENUM_VARIANTS: usize = 3;
807+
808+
fn write_enum_message(
809+
f: &mut Formatter<'_>,
810+
value: impl fmt::Display,
811+
options: &Value,
812+
) -> fmt::Result {
813+
let array = options
814+
.as_array()
815+
.expect("Enum options must be a JSON array");
816+
817+
write!(f, "{value} is not one of ")?;
818+
819+
let total_count = array.len();
820+
821+
if total_count <= MAX_DISPLAYED_ENUM_VARIANTS {
822+
// Show all options with proper "a, b or c" formatting
823+
for (i, option) in array.iter().enumerate() {
824+
if i == 0 {
825+
write!(f, "{option}")?;
826+
} else if i == total_count - 1 {
827+
write!(f, " or {option}")?;
828+
} else {
829+
write!(f, ", {option}")?;
830+
}
831+
}
832+
} else {
833+
// Show first few, then "or X other candidates"
834+
let show_count = MAX_DISPLAYED_ENUM_VARIANTS - 1;
835+
for (i, option) in array.iter().take(show_count).enumerate() {
836+
if i == 0 {
837+
write!(f, "{option}")?;
838+
} else {
839+
write!(f, ", {option}")?;
840+
}
841+
}
842+
let remaining = total_count - show_count;
843+
write!(f, " or {remaining} other candidates")?;
844+
}
845+
Ok(())
846+
}
847+
806848
/// Textual representation of various validation errors.
807849
impl fmt::Display for ValidationError<'_> {
808850
#[allow(clippy::too_many_lines)] // The function is long but it does formatting only
@@ -866,9 +908,7 @@ impl fmt::Display for ValidationError<'_> {
866908
)
867909
}
868910
ValidationErrorKind::FromUtf8 { error } => error.fmt(f),
869-
ValidationErrorKind::Enum { options } => {
870-
write!(f, "{} is not one of {}", self.instance, options)
871-
}
911+
ValidationErrorKind::Enum { options } => write_enum_message(f, &self.instance, options),
872912
ValidationErrorKind::ExclusiveMaximum { limit } => write!(
873913
f,
874914
"{} is greater than or equal to the maximum of {}",
@@ -1046,7 +1086,7 @@ impl fmt::Display for MaskedValidationError<'_, '_, '_> {
10461086
}
10471087
ValidationErrorKind::FromUtf8 { error } => error.fmt(f),
10481088
ValidationErrorKind::Enum { options } => {
1049-
write!(f, "{} is not one of {}", self.placeholder, options)
1089+
write_enum_message(f, &self.placeholder, options)
10501090
}
10511091
ValidationErrorKind::ExclusiveMaximum { limit } => write!(
10521092
f,

crates/jsonschema/src/keywords/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,11 @@ mod tests {
400400
#[test_case(&json!({"anyOf": [{"type": "integer"}, {"minimum": 2}]}), &json!(1.5), r"1.5 is not valid under any of the schemas listed in the 'anyOf' keyword")]
401401
#[test_case(&json!({"const": 2}), &json!(5), r"2 was expected")]
402402
#[test_case(&json!({"contains": {"minimum": 5}}), &json!([2, 3, 4]), r"None of [2,3,4] are valid under the given schema")]
403-
#[test_case(&json!({"enum": [1, 2, 3]}), &json!(4), r"4 is not one of [1,2,3]")]
403+
#[test_case(&json!({"enum": [1]}), &json!(4), r"4 is not one of 1")]
404+
#[test_case(&json!({"enum": [1, 2]}), &json!(4), r"4 is not one of 1 or 2")]
405+
#[test_case(&json!({"enum": [1, 2, 3]}), &json!(4), r"4 is not one of 1, 2 or 3")]
406+
#[test_case(&json!({"enum": [1, 2, 3, 4]}), &json!(5), r"5 is not one of 1, 2 or 2 other candidates")]
407+
#[test_case(&json!({"enum": [1, 2, 3, 4, 5]}), &json!(6), r"6 is not one of 1, 2 or 3 other candidates")]
404408
#[test_case(&json!({"exclusiveMaximum": 3}), &json!(3.0), r"3.0 is greater than or equal to the maximum of 3")]
405409
#[test_case(&json!({"exclusiveMaximum": 3.0}), &json!(3.0), r"3.0 is greater than or equal to the maximum of 3.0")]
406410
#[test_case(&json!({"exclusiveMinimum": 1}), &json!(1.0), r"1.0 is less than or equal to the minimum of 1")]

0 commit comments

Comments
 (0)