metrale_grammar/schema/error.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// Error type for the JSON-schema -> EBNF converter — port of the
4// internal `SchemaError` / `SchemaErrorType` from
5// `cpp/json_schema_converter.cc`.
6//
7// The C++ converter aborts the process (`XGRAMMAR_LOG(FATAL)`) on a
8// malformed schema; here we return `Err(SchemaError)` — no panics.
9
10use std::fmt;
11
12/// Classification of a schema-conversion failure, mirroring the C++
13/// `SchemaErrorType` enum.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SchemaErrorKind {
16 /// The schema document is structurally invalid (wrong JSON type
17 /// for a keyword, non-string `type`, unparsable JSON, ...).
18 InvalidSchema,
19 /// The schema is well-formed but cannot accept any value (e.g.
20 /// `false`, `minItems > maxItems`).
21 UnsatisfiableSchema,
22 /// A `$ref` could not be resolved or a regex/sub-conversion
23 /// failed. (`InvalidSchema` is reused in C++; we keep a dedicated
24 /// variant to make ref failures easy to spot.)
25 RefResolution,
26}
27
28/// An error raised while converting a JSON Schema document into an
29/// EBNF grammar string.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct SchemaError {
32 /// The category of the failure.
33 pub kind: SchemaErrorKind,
34 /// Human-readable description.
35 pub message: String,
36}
37
38impl SchemaError {
39 /// Construct an `InvalidSchema` error.
40 pub fn invalid(message: impl Into<String>) -> Self {
41 SchemaError {
42 kind: SchemaErrorKind::InvalidSchema,
43 message: message.into(),
44 }
45 }
46
47 /// Construct an `UnsatisfiableSchema` error.
48 pub fn unsatisfiable(message: impl Into<String>) -> Self {
49 SchemaError {
50 kind: SchemaErrorKind::UnsatisfiableSchema,
51 message: message.into(),
52 }
53 }
54
55 /// Construct a `RefResolution` error.
56 pub fn ref_error(message: impl Into<String>) -> Self {
57 SchemaError {
58 kind: SchemaErrorKind::RefResolution,
59 message: message.into(),
60 }
61 }
62}
63
64impl fmt::Display for SchemaError {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 let tag = match self.kind {
67 SchemaErrorKind::InvalidSchema => "invalid schema",
68 SchemaErrorKind::UnsatisfiableSchema => "unsatisfiable schema",
69 SchemaErrorKind::RefResolution => "ref resolution",
70 };
71 write!(f, "{tag}: {}", self.message)
72 }
73}
74
75impl std::error::Error for SchemaError {}
76
77/// Convenience alias for converter results.
78pub type SchemaResult<T> = Result<T, SchemaError>;