metrale_grammar/schema/options.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// Conversion options and the root output format — ports of the
4// `JSONFormat` enum and the `JSONSchemaToEBNF` keyword arguments from
5// `cpp/json_schema_converter.h`.
6
7/// Root output format. `Json` produces a fully JSON-style grammar;
8/// the XML variants produce an XML-style root object with JSON-style
9/// inner values (used for tool calling). Mirrors C++ `JSONFormat`.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum JsonFormat {
12 /// Standard JSON grammar.
13 Json,
14 /// Qwen XML tool-calling style: `<parameter=name>value</parameter>`.
15 QwenXml,
16 /// MiniMax XML tool-calling style.
17 MiniMaxXml,
18 /// DeepSeek XML tool-calling style.
19 DeepSeekXml,
20}
21
22impl JsonFormat {
23 /// Whether this is one of the XML tool-calling formats.
24 pub fn is_xml(self) -> bool {
25 !matches!(self, JsonFormat::Json)
26 }
27}
28
29/// Options controlling JSON-schema -> EBNF conversion. Field meanings
30/// match the `JSONSchemaToEBNF` parameters in the C++ header.
31#[derive(Debug, Clone)]
32pub struct SchemaConverterOptions {
33 /// Allow any whitespace between JSON tokens instead of enforcing
34 /// the indentation/separator restrictions. Default `true`.
35 pub any_whitespace: bool,
36 /// Number of spaces per indent level. `None` => single-line.
37 pub indent: Option<i32>,
38 /// `(comma, colon)` separators. `None` => derived defaults.
39 pub separators: Option<(String, String)>,
40 /// Strict mode disallows properties/items not named in the schema
41 /// (equivalent to `unevaluatedProperties:false` /
42 /// `unevaluatedItems:false`). Default `true`.
43 pub strict_mode: bool,
44 /// Cap on whitespace characters when `any_whitespace` is set.
45 /// `None` => unlimited.
46 pub max_whitespace_cnt: Option<i32>,
47 /// Root output format.
48 pub json_format: JsonFormat,
49}
50
51impl Default for SchemaConverterOptions {
52 fn default() -> Self {
53 SchemaConverterOptions {
54 any_whitespace: true,
55 indent: None,
56 separators: None,
57 strict_mode: true,
58 max_whitespace_cnt: None,
59 json_format: JsonFormat::Json,
60 }
61 }
62}
63
64impl SchemaConverterOptions {
65 /// Resolve the `(comma, colon)` separator pair, applying the same
66 /// defaulting logic as the C++ `JSONSchemaConverter` constructor.
67 pub fn resolved_separators(&self) -> (String, String) {
68 if let Some((c, col)) = &self.separators {
69 return (c.clone(), col.clone());
70 }
71 // No space after the comma when whitespace is flexible or an
72 // explicit indent is set (the indent supplies the separation).
73 let comma = if self.any_whitespace || self.indent.is_some() {
74 ","
75 } else {
76 ", "
77 };
78 let colon = if self.any_whitespace { ":" } else { ": " };
79 (comma.to_string(), colon.to_string())
80 }
81}