1
1
//! GraphQL support for [`serde_json::Value`].
2
2
3
+ use std:: marker:: PhantomData ;
4
+
3
5
use graphql_parser:: {
4
6
parse_schema,
5
7
query:: { Text , Type } ,
6
8
schema:: { Definition , TypeDefinition } ,
7
9
} ;
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
- } ;
15
10
use serde_json:: Value as Json ;
16
11
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
+ } ;
17
19
18
20
// Used to describe the graphql type of a `serde_json::Value` using the GraphQL
19
21
// schema definition language.
@@ -337,19 +339,89 @@ impl<S> GraphQLValueAsync<S> for Json
337
339
}
338
340
}
339
341
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
+ }
340
413
341
414
#[ cfg( test) ]
342
415
mod 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
+
351
418
use serde_json:: json;
352
419
420
+ use juniper:: {
421
+ integrations:: json:: { TypedJson , TypedJsonInfo , TypeInfo } ,
422
+ EmptyMutation , EmptySubscription , execute_sync, FieldResult , graphql_object, graphql_value,
423
+ RootNode , ToInputValue , Variables ,
424
+ } ;
353
425
354
426
#[ test]
355
427
fn sdl_type_info ( ) {
@@ -588,51 +660,27 @@ mod tests {
588
660
#[ test]
589
661
fn test_as_field_of_output_type ( ) {
590
662
// 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
614
665
{
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"
619
668
}
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
+ "#
627
675
}
628
676
}
629
677
630
678
struct Query ;
631
679
#[ graphql_object( ) ]
632
680
impl Query {
633
- fn foo ( ) -> FieldResult < Foo > {
681
+ fn foo ( ) -> FieldResult < TypedJson < Foo > > {
634
682
let data = json ! ( { "message" : [ "Hello" , "World" ] } ) ;
635
- Ok ( Foo ( data) )
683
+ Ok ( TypedJson { value : data, phantom : PhantomData } )
636
684
}
637
685
}
638
686
let schema = juniper:: RootNode :: new ( Query , EmptyMutation :: new ( ) , EmptySubscription :: new ( ) ) ;
@@ -657,58 +705,27 @@ mod tests {
657
705
658
706
#[ test]
659
707
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
-
663
708
#[ 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
673
711
{
674
- fn name ( _info : & Self :: TypeInfo ) -> Option < & str > {
675
- Some ( "Foo" )
712
+ fn type_name ( ) -> & ' static str {
713
+ "Foo"
676
714
}
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
+ "#
704
721
}
705
722
}
706
723
707
724
struct Query ;
708
725
#[ graphql_object( ) ]
709
726
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 } )
712
729
}
713
730
}
714
731
let schema = juniper:: RootNode :: new ( Query , EmptyMutation :: new ( ) , EmptySubscription :: new ( ) ) ;
@@ -738,3 +755,4 @@ mod tests {
738
755
}
739
756
}
740
757
758
+
0 commit comments