11//! GraphQL support for [`serde_json::Value`].
22
3+ use std:: marker:: PhantomData ;
4+
35use graphql_parser:: {
46 parse_schema,
57 query:: { Text , Type } ,
68 schema:: { Definition , TypeDefinition } ,
79} ;
8- use juniper:: {
9- marker:: { IsOutputType , IsInputType } ,
10- meta:: { Field , MetaType , Argument } ,
11- types:: base:: resolve_selection_set_into,
12- Arguments , ExecutionResult , Executor , FieldError , GraphQLType , GraphQLValue , Registry ,
13- ScalarValue , Selection , Value , GraphQLValueAsync , BoxFuture , FromInputValue , InputValue ,
14- } ;
1510use serde_json:: Value as Json ;
1611
12+ use juniper:: {
13+ Arguments ,
14+ BoxFuture ,
15+ ExecutionResult ,
16+ Executor , FieldError , FromInputValue , GraphQLType , GraphQLValue , GraphQLValueAsync , InputValue ,
17+ marker:: { IsInputType , IsOutputType } , meta:: { Argument , Field , MetaType } , Registry , ScalarValue , Selection , types:: base:: resolve_selection_set_into, Value ,
18+ } ;
1719
1820// Used to describe the graphql type of a `serde_json::Value` using the GraphQL
1921// schema definition language.
@@ -337,19 +339,89 @@ impl<S> GraphQLValueAsync<S> for Json
337339 }
338340}
339341
342+ trait TypedJsonInfo : Send + Sync {
343+ fn type_name ( ) -> & ' static str ;
344+ fn schema ( ) -> & ' static str ;
345+ }
346+
347+ #[ derive( Debug , Clone , PartialEq ) ]
348+ struct TypedJson < T : TypedJsonInfo > {
349+ value : serde_json:: Value ,
350+ phantom : PhantomData < T > ,
351+ }
352+
353+ impl < T , S > IsOutputType < S > for TypedJson < T > where
354+ S : ScalarValue ,
355+ T : TypedJsonInfo ,
356+ { }
357+
358+ impl < T , S > IsInputType < S > for TypedJson < T > where
359+ S : ScalarValue ,
360+ T : TypedJsonInfo ,
361+ { }
362+
363+ impl < T , S > FromInputValue < S > for TypedJson < T > where
364+ S : ScalarValue ,
365+ T : TypedJsonInfo ,
366+ {
367+ fn from_input_value ( v : & InputValue < S > ) -> Option < Self > {
368+ <serde_json:: Value as FromInputValue < S > >:: from_input_value ( v) . map ( |x| TypedJson { value : x, phantom : PhantomData } )
369+ }
370+ }
371+
372+ impl < T , S > GraphQLValueAsync < S > for TypedJson < T > where
373+ S : ScalarValue + Send + Sync ,
374+ T : TypedJsonInfo
375+ { }
376+
377+ impl < T , S > GraphQLType < S > for TypedJson < T > where
378+ S : ScalarValue ,
379+ T : TypedJsonInfo ,
380+ {
381+ fn name ( _info : & Self :: TypeInfo ) -> Option < & str > {
382+ Some ( T :: type_name ( ) )
383+ }
384+ fn meta < ' r > ( _info : & Self :: TypeInfo , registry : & mut Registry < ' r , S > ) -> MetaType < ' r , S >
385+ where S : ' r ,
386+ {
387+ TypeInfo
388+ {
389+ name : T :: type_name ( ) . to_string ( ) ,
390+ schema : Some ( T :: schema ( ) . to_string ( ) ) ,
391+ } . meta ( registry)
392+ }
393+ }
394+
395+ impl < T , S > GraphQLValue < S > for TypedJson < T >
396+ where S : ScalarValue ,
397+ T : TypedJsonInfo ,
398+ {
399+ type Context = ( ) ;
400+ type TypeInfo = ( ) ;
401+ fn type_name < ' i > ( & self , _info : & ' i Self :: TypeInfo ) -> Option < & ' i str > {
402+ Some ( T :: type_name ( ) )
403+ }
404+ fn resolve (
405+ & self ,
406+ _info : & Self :: TypeInfo ,
407+ _selection : Option < & [ Selection < S > ] > ,
408+ executor : & Executor < Self :: Context , S > ,
409+ ) -> ExecutionResult < S > {
410+ executor. resolve ( & TypeInfo { schema : None , name : T :: type_name ( ) . to_string ( ) } , & self . value )
411+ }
412+ }
340413
341414#[ cfg( test) ]
342415mod tests {
343- use juniper:: {
344- marker:: { IsOutputType , IsInputType } ,
345- meta:: MetaType ,
346- integrations:: json:: TypeInfo ,
347- execute_sync, graphql_object, graphql_value, EmptyMutation , EmptySubscription , RootNode , Variables ,
348- ScalarValue , GraphQLValue , GraphQLType , Selection , Executor , ExecutionResult , FieldResult ,
349- GraphQLValueAsync , Registry , ToInputValue , FromInputValue , InputValue ,
350- } ;
416+ use std:: marker:: PhantomData ;
417+
351418 use serde_json:: json;
352419
420+ use juniper:: {
421+ integrations:: json:: { TypedJson , TypedJsonInfo , TypeInfo } ,
422+ EmptyMutation , EmptySubscription , execute_sync, FieldResult , graphql_object, graphql_value,
423+ RootNode , ToInputValue , Variables ,
424+ } ;
353425
354426 #[ test]
355427 fn sdl_type_info ( ) {
@@ -588,51 +660,27 @@ mod tests {
588660 #[ test]
589661 fn test_as_field_of_output_type ( ) {
590662 // We need a Foo wrapper associate a static SDL to the Foo type which
591- // wraps the serde_json::Value. Would be nice if a macro could code gen this.
592- struct Foo ( serde_json:: Value ) ;
593- impl < S > IsOutputType < S > for Foo where S : ScalarValue { }
594- impl < S > GraphQLValueAsync < S > for Foo where S : ScalarValue + Send + Sync { }
595- impl < S > GraphQLType < S > for Foo where S : ScalarValue
596- {
597- fn name ( _info : & Self :: TypeInfo ) -> Option < & str > {
598- Some ( "Foo" )
599- }
600- fn meta < ' r > ( _info : & Self :: TypeInfo , registry : & mut Registry < ' r , S > ) -> MetaType < ' r , S >
601- where S : ' r ,
602- {
603- TypeInfo {
604- name : "Foo" . to_string ( ) ,
605- schema : Some ( r#"
606- type Foo {
607- message: [String]
608- }
609- "# . to_string ( ) ) ,
610- } . meta ( registry)
611- }
612- }
613- impl < S > GraphQLValue < S > for Foo where S : ScalarValue
663+ struct Foo ;
664+ impl TypedJsonInfo for Foo
614665 {
615- type Context = ( ) ;
616- type TypeInfo = ( ) ;
617- fn type_name < ' i > ( & self , info : & ' i Self :: TypeInfo ) -> Option < & ' i str > {
618- <Self as GraphQLType >:: name ( info)
666+ fn type_name ( ) -> & ' static str {
667+ "Foo"
619668 }
620- fn resolve (
621- & self ,
622- _info : & Self :: TypeInfo ,
623- _selection : Option < & [ Selection < S > ] > ,
624- executor : & Executor < Self :: Context , S > ,
625- ) -> ExecutionResult < S > {
626- executor. resolve ( & TypeInfo { schema : None , name : "Foo" . to_string ( ) } , & self . 0 )
669+ fn schema ( ) -> & ' static str {
670+ r#"
671+ type Foo {
672+ message: [String]
673+ }
674+ "#
627675 }
628676 }
629677
630678 struct Query ;
631679 #[ graphql_object( ) ]
632680 impl Query {
633- fn foo ( ) -> FieldResult < Foo > {
681+ fn foo ( ) -> FieldResult < TypedJson < Foo > > {
634682 let data = json ! ( { "message" : [ "Hello" , "World" ] } ) ;
635- Ok ( Foo ( data) )
683+ Ok ( TypedJson { value : data, phantom : PhantomData } )
636684 }
637685 }
638686 let schema = juniper:: RootNode :: new ( Query , EmptyMutation :: new ( ) , EmptySubscription :: new ( ) ) ;
@@ -657,58 +705,27 @@ mod tests {
657705
658706 #[ test]
659707 fn test_as_field_of_input_type ( ) {
660- // We need a Foo wrapper associate a static SDL to the Foo type which
661- // wraps the serde_json::Value. Would be nice if a macro could code gen this.
662-
663708 #[ derive( Debug , Clone , PartialEq ) ]
664- struct Foo ( serde_json:: Value ) ;
665- impl < S > IsInputType < S > for Foo where S : ScalarValue { }
666- impl < S > GraphQLValueAsync < S > for Foo where S : ScalarValue + Send + Sync { }
667- impl < S > FromInputValue < S > for Foo where S : ScalarValue {
668- fn from_input_value ( v : & InputValue < S > ) -> Option < Self > {
669- <serde_json:: Value as FromInputValue < S > >:: from_input_value ( v) . map ( |x| Foo ( x) )
670- }
671- }
672- impl < S > GraphQLType < S > for Foo where S : ScalarValue
709+ struct Foo ;
710+ impl TypedJsonInfo for Foo
673711 {
674- fn name ( _info : & Self :: TypeInfo ) -> Option < & str > {
675- Some ( "Foo" )
712+ fn type_name ( ) -> & ' static str {
713+ "Foo"
676714 }
677- fn meta < ' r > ( _info : & Self :: TypeInfo , registry : & mut Registry < ' r , S > ) -> MetaType < ' r , S >
678- where S : ' r ,
679- {
680- TypeInfo {
681- name : "Foo" . to_string ( ) ,
682- schema : Some ( r#"
683- input Foo {
684- message: [String]
685- }
686- "# . to_string ( ) ) ,
687- } . meta ( registry)
688- }
689- }
690- impl < S > GraphQLValue < S > for Foo where S : ScalarValue
691- {
692- type Context = ( ) ;
693- type TypeInfo = ( ) ;
694- fn type_name < ' i > ( & self , info : & ' i Self :: TypeInfo ) -> Option < & ' i str > {
695- <Self as GraphQLType >:: name ( info)
696- }
697- fn resolve (
698- & self ,
699- _info : & Self :: TypeInfo ,
700- _selection : Option < & [ Selection < S > ] > ,
701- executor : & Executor < Self :: Context , S > ,
702- ) -> ExecutionResult < S > {
703- executor. resolve ( & TypeInfo { schema : None , name : "Foo" . to_string ( ) } , & self . 0 )
715+ fn schema ( ) -> & ' static str {
716+ r#"
717+ input Foo {
718+ message: [String]
719+ }
720+ "#
704721 }
705722 }
706723
707724 struct Query ;
708725 #[ graphql_object( ) ]
709726 impl Query {
710- fn foo ( value : Foo ) -> FieldResult < bool > {
711- Ok ( value == Foo ( json ! ( { "message" : [ "Hello" , "World" ] } ) ) )
727+ fn foo ( value : TypedJson < Foo > ) -> FieldResult < bool > {
728+ Ok ( value == TypedJson { value : json ! ( { "message" : [ "Hello" , "World" ] } ) , phantom : PhantomData } )
712729 }
713730 }
714731 let schema = juniper:: RootNode :: new ( Query , EmptyMutation :: new ( ) , EmptySubscription :: new ( ) ) ;
@@ -738,3 +755,4 @@ mod tests {
738755 }
739756}
740757
758+
0 commit comments