llama_cpp_bindings/
json_schema_to_grammar.rs1use std::ffi::{CStr, CString, c_char};
2
3use crate::error::JsonSchemaToGrammarError;
4use crate::ffi_error_reader::read_and_free_cpp_error;
5
6pub fn json_schema_to_grammar(schema_json: &str) -> Result<String, JsonSchemaToGrammarError> {
11 let schema_cstr = CString::new(schema_json)?;
12 let mut out: *mut c_char = std::ptr::null_mut();
13 let mut error_ptr: *mut c_char = std::ptr::null_mut();
14
15 let status = unsafe {
16 llama_cpp_bindings_sys::llama_rs_json_schema_to_grammar(
17 schema_cstr.as_ptr(),
18 false,
19 &raw mut out,
20 &raw mut error_ptr,
21 )
22 };
23
24 match status {
25 llama_cpp_bindings_sys::LLAMA_RS_JSON_SCHEMA_TO_GRAMMAR_OK => {
26 let grammar_bytes = unsafe { CStr::from_ptr(out) }.to_bytes().to_vec();
27 unsafe { llama_cpp_bindings_sys::llama_rs_string_free(out) };
28 Ok(String::from_utf8(grammar_bytes)?)
29 }
30 llama_cpp_bindings_sys::LLAMA_RS_JSON_SCHEMA_TO_GRAMMAR_ERROR_STRING_ALLOCATION_FAILED => {
31 Err(JsonSchemaToGrammarError::NotEnoughMemory)
32 }
33 llama_cpp_bindings_sys::LLAMA_RS_JSON_SCHEMA_TO_GRAMMAR_INVALID_SCHEMA => {
34 let message = unsafe { read_and_free_cpp_error(error_ptr) };
35 Err(JsonSchemaToGrammarError::InvalidSchema { message })
36 }
37 llama_cpp_bindings_sys::LLAMA_RS_JSON_SCHEMA_TO_GRAMMAR_VENDORED_THREW_CXX_EXCEPTION => {
38 let message = unsafe { read_and_free_cpp_error(error_ptr) };
39 Err(JsonSchemaToGrammarError::Reported { message })
40 }
41 other => {
42 unreachable!("llama_rs_json_schema_to_grammar returned unrecognized status {other}")
43 }
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::json_schema_to_grammar;
50 use crate::error::JsonSchemaToGrammarError;
51
52 #[test]
53 fn simple_object() {
54 let schema = r#"{"type": "object", "properties": {"name": {"type": "string"}}}"#;
55 let grammar = json_schema_to_grammar(schema).expect("schema converts to grammar");
56
57 assert!(!grammar.is_empty());
58 }
59
60 #[test]
61 fn null_byte_returns_schema_contains_nul_byte_error() {
62 use std::ffi::CString;
63
64 let schema = "{\x00}";
65 let err = json_schema_to_grammar(schema).unwrap_err();
66 let representative = JsonSchemaToGrammarError::SchemaContainsNulByte(
67 CString::new(b"a\0b".to_vec()).unwrap_err(),
68 );
69
70 assert_eq!(
71 std::mem::discriminant(&err),
72 std::mem::discriminant(&representative)
73 );
74 }
75
76 #[test]
77 fn simple_string() {
78 let schema = r#"{"type": "string"}"#;
79 let grammar = json_schema_to_grammar(schema).expect("schema converts to grammar");
80
81 assert!(!grammar.is_empty());
82 }
83
84 #[test]
85 fn invalid_json_returns_reported() {
86 let schema = "not valid json at all";
87 let err = json_schema_to_grammar(schema).unwrap_err();
88 let representative = JsonSchemaToGrammarError::Reported {
89 message: String::new(),
90 };
91
92 assert_eq!(
93 std::mem::discriminant(&err),
94 std::mem::discriminant(&representative)
95 );
96 }
97
98 #[test]
99 fn unresolved_ref_returns_invalid_schema() {
100 let schema = r##"{"$ref": "#/$defs/Missing"}"##;
101 let err = json_schema_to_grammar(schema).unwrap_err();
102 let representative = JsonSchemaToGrammarError::InvalidSchema {
103 message: String::new(),
104 };
105
106 assert_eq!(
107 std::mem::discriminant(&err),
108 std::mem::discriminant(&representative)
109 );
110 }
111}