1- use crate :: utils :: immutable :: RefList ;
1+ use crate :: prelude :: * ;
22
3- use super :: { schema, spec:: FieldName } ;
4- use anyhow:: Result ;
5- use indexmap:: IndexMap ;
3+ use crate :: utils:: immutable:: RefList ;
64use schemars:: schema:: {
75 ArrayValidation , InstanceType , ObjectValidation , Schema , SchemaObject , SingleOrVec ,
86} ;
@@ -19,6 +17,9 @@ pub struct ToJsonSchemaOptions {
1917
2018 /// If true, extract descriptions to a separate extra instruction.
2119 pub extract_descriptions : bool ,
20+
21+ /// If true, the top level must be a JSON object.
22+ pub top_level_must_be_object : bool ,
2223}
2324
2425struct JsonSchemaBuilder {
@@ -38,7 +39,7 @@ impl JsonSchemaBuilder {
3839 & mut self ,
3940 schema : & mut SchemaObject ,
4041 description : impl ToString ,
41- field_path : RefList < ' _ , & ' _ FieldName > ,
42+ field_path : RefList < ' _ , & ' _ spec :: FieldName > ,
4243 ) {
4344 if self . options . extract_descriptions {
4445 let mut fields: Vec < _ > = field_path. iter ( ) . map ( |f| f. as_str ( ) ) . collect ( ) ;
@@ -53,7 +54,7 @@ impl JsonSchemaBuilder {
5354 fn for_basic_value_type (
5455 & mut self ,
5556 basic_type : & schema:: BasicValueType ,
56- field_path : RefList < ' _ , & ' _ FieldName > ,
57+ field_path : RefList < ' _ , & ' _ spec :: FieldName > ,
5758 ) -> SchemaObject {
5859 let mut schema = SchemaObject :: default ( ) ;
5960 match basic_type {
@@ -171,7 +172,7 @@ impl JsonSchemaBuilder {
171172 fn for_struct_schema (
172173 & mut self ,
173174 struct_schema : & schema:: StructSchema ,
174- field_path : RefList < ' _ , & ' _ FieldName > ,
175+ field_path : RefList < ' _ , & ' _ spec :: FieldName > ,
175176 ) -> SchemaObject {
176177 let mut schema = SchemaObject :: default ( ) ;
177178 if let Some ( description) = & struct_schema. description {
@@ -213,7 +214,7 @@ impl JsonSchemaBuilder {
213214 fn for_value_type (
214215 & mut self ,
215216 value_type : & schema:: ValueType ,
216- field_path : RefList < ' _ , & ' _ FieldName > ,
217+ field_path : RefList < ' _ , & ' _ spec :: FieldName > ,
217218 ) -> SchemaObject {
218219 match value_type {
219220 schema:: ValueType :: Basic ( b) => self . for_basic_value_type ( b, field_path) ,
@@ -234,7 +235,7 @@ impl JsonSchemaBuilder {
234235 fn for_enriched_value_type (
235236 & mut self ,
236237 enriched_value_type : & schema:: EnrichedValueType ,
237- field_path : RefList < ' _ , & ' _ FieldName > ,
238+ field_path : RefList < ' _ , & ' _ spec :: FieldName > ,
238239 ) -> SchemaObject {
239240 self . for_value_type ( & enriched_value_type. typ , field_path)
240241 }
@@ -262,11 +263,69 @@ impl JsonSchemaBuilder {
262263 }
263264}
264265
266+ pub struct ValueExtractor {
267+ value_type : schema:: ValueType ,
268+ object_wrapper_field_name : Option < String > ,
269+ }
270+
271+ impl ValueExtractor {
272+ pub fn extract_value ( & self , json_value : serde_json:: Value ) -> Result < value:: Value > {
273+ let unwrapped_json_value =
274+ if let Some ( object_wrapper_field_name) = & self . object_wrapper_field_name {
275+ match json_value {
276+ serde_json:: Value :: Object ( mut o) => o
277+ . remove ( object_wrapper_field_name)
278+ . unwrap_or ( serde_json:: Value :: Null ) ,
279+ _ => {
280+ bail ! ( "Field `{}` not found" , object_wrapper_field_name)
281+ }
282+ }
283+ } else {
284+ json_value
285+ } ;
286+ let result = value:: Value :: from_json ( unwrapped_json_value, & self . value_type ) ?;
287+ Ok ( result)
288+ }
289+ }
290+
291+ pub struct BuildJsonSchemaOutput {
292+ pub schema : SchemaObject ,
293+ pub extra_instructions : Option < String > ,
294+ pub value_extractor : ValueExtractor ,
295+ }
296+
265297pub fn build_json_schema (
266- value_type : & schema:: EnrichedValueType ,
298+ value_type : schema:: EnrichedValueType ,
267299 options : ToJsonSchemaOptions ,
268- ) -> Result < ( SchemaObject , Option < String > ) > {
300+ ) -> Result < BuildJsonSchemaOutput > {
269301 let mut builder = JsonSchemaBuilder :: new ( options) ;
270- let schema = builder. for_enriched_value_type ( value_type, RefList :: Nil ) ;
271- Ok ( ( schema, builder. build_extra_instructions ( ) ?) )
302+ let ( schema, object_wrapper_field_name) = if builder. options . top_level_must_be_object
303+ && !matches ! ( value_type. typ, schema:: ValueType :: Struct ( _) )
304+ {
305+ let object_wrapper_field_name = "value" . to_string ( ) ;
306+ let wrapper_struct = schema:: StructSchema {
307+ fields : Arc :: new ( vec ! [ schema:: FieldSchema {
308+ name: object_wrapper_field_name. clone( ) ,
309+ value_type: value_type. clone( ) ,
310+ } ] ) ,
311+ description : None ,
312+ } ;
313+ (
314+ builder. for_struct_schema ( & wrapper_struct, RefList :: Nil ) ,
315+ Some ( object_wrapper_field_name) ,
316+ )
317+ } else {
318+ (
319+ builder. for_enriched_value_type ( & value_type, RefList :: Nil ) ,
320+ None ,
321+ )
322+ } ;
323+ Ok ( BuildJsonSchemaOutput {
324+ schema,
325+ extra_instructions : builder. build_extra_instructions ( ) ?,
326+ value_extractor : ValueExtractor {
327+ value_type : value_type. typ ,
328+ object_wrapper_field_name,
329+ } ,
330+ } )
272331}
0 commit comments