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}