1mod structs {
12 pub mod object_type;
13 pub mod create_input;
14 pub mod read_input;
15 pub mod read_boolean_input;
16 pub mod read_date_input;
17 pub mod read_float_input;
18 pub mod read_id_input;
19 pub mod read_int_input;
20 pub mod read_string_input;
21 pub mod read_enum_input;
22 pub mod read_relation_input;
23 pub mod read_json_input;
24 pub mod read_blob_input;
25 pub mod order_input;
26 pub mod update_input;
27 pub mod delete_input;
28 pub mod upsert_input;
29}
30mod query_resolvers {
31 pub mod read;
32}
33mod mutation_resolvers {
34 pub mod create;
35 pub mod update;
36 pub mod delete;
37 pub mod upsert;
38 pub mod init;
39}
40mod settings {
41 pub mod generate_settings;
42}
43mod custom_resolvers {
44 pub mod generate_custom_query_struct;
45 pub mod generate_custom_mutation_struct;
46 pub mod utilities;
47}
48mod enums {
49 pub mod enum_type;
50}
51
52use proc_macro::TokenStream;
53use quote::quote;
54use syn::{
55 parse_macro_input,
56 LitStr
57};
58use std::{
59 fs
60};
61use graphql_parser::schema::{
62 parse_schema,
63 Definition,
64 TypeDefinition,
65 ObjectType,
66 Type,
67 Document,
68 Field,
69 EnumType
70};
71use structs::object_type::generate_object_type_structs;
72use structs::create_input::generate_create_input_rust_structs;
73use structs::read_input::generate_read_input_rust_structs;
74use structs::read_boolean_input::get_read_boolean_input_rust_struct;
75use structs::read_date_input::get_read_date_input_rust_struct;
76use structs::read_float_input::get_read_float_input_rust_struct;
77use structs::read_id_input::get_read_id_input_rust_struct;
78use structs::read_int_input::get_read_int_input_rust_struct;
79use structs::read_string_input::get_read_string_input_rust_struct;
80use structs::read_enum_input::get_read_enum_input_rust_struct;
81use structs::read_relation_input::get_read_relation_input_rust_struct;
82use structs::read_json_input::get_read_json_input_rust_struct;
83use structs::read_blob_input::get_read_blob_input_rust_struct;
84use structs::order_input::generate_order_input_rust_structs;
85use structs::update_input::generate_update_input_rust_structs;
86use structs::delete_input::generate_delete_input_rust_structs;
87use structs::upsert_input::generate_upsert_input_rust_structs;
88use enums::enum_type::generate_enums;
89use query_resolvers::read::generate_read_query_resolvers;
90use mutation_resolvers::create::generate_create_mutation_resolvers;
91use mutation_resolvers::update::generate_update_mutation_resolvers;
92use mutation_resolvers::delete::generate_delete_mutation_resolvers;
93use mutation_resolvers::upsert::generate_upsert_mutation_resolvers;
94use mutation_resolvers::init::generate_init_mutation_resolvers;
95use settings::generate_settings::{
96 generate_export_generated_query_function_attribute,
97 generate_export_generated_mutation_function_attribute,
98 generate_export_generated_init_function_attribute,
99 generate_export_generated_post_upgrade_function_attribute,
100 generate_clear_mutation
101};
102use custom_resolvers::{
103 generate_custom_query_struct::{
104 generate_merged_query_object_names,
105 generate_custom_query_struct
106 },
107 generate_custom_mutation_struct::{
108 generate_merged_mutation_object_names,
109 generate_custom_mutation_struct
110 }
111};
112
113#[proc_macro]
114pub fn graphql_database(schema_file_path_token_stream: TokenStream) -> TokenStream {
115 let schema_file_path_string_literal = parse_macro_input!(schema_file_path_token_stream as LitStr);
116 let schema_file_path_string_value = schema_file_path_string_literal.value();
117
118 let cwd = std::env::current_dir().expect("graphql_database::cwd");
126 let schema_absolute_file_path = cwd.join(&schema_file_path_string_value);
127 let schema_absolute_file_path_string_option = schema_absolute_file_path.to_str();
128 let schema_absolute_file_path_string = schema_absolute_file_path_string_option.unwrap();
129
130 let schema_file_contents = fs::read_to_string(&schema_absolute_file_path_string).unwrap();
131
132 let graphql_ast = parse_schema::<String>(&schema_file_contents).unwrap();
133
134 let all_object_types = get_object_types(
135 &graphql_ast
136 );
137
138 let sudograph_settings_option = all_object_types.iter().find(|object_type| {
139 return object_type.name == "SudographSettings";
140 });
141
142 let export_generated_query_function_attribute = generate_export_generated_query_function_attribute(sudograph_settings_option);
143 let export_generated_mutation_function_attribute = generate_export_generated_mutation_function_attribute(sudograph_settings_option);
144 let export_generated_init_function_attribute = generate_export_generated_init_function_attribute(sudograph_settings_option);
145 let export_generated_post_upgrade_function_attribute = generate_export_generated_post_upgrade_function_attribute(sudograph_settings_option);
146
147 let clear_mutation = generate_clear_mutation(sudograph_settings_option);
148
149 let query_object_option = all_object_types.iter().find(|object_type| {
150 return object_type.name == "Query";
151 });
152
153 let mutation_object_option = all_object_types.iter().find(|object_type| {
154 return object_type.name == "Mutation";
155 });
156
157 let generated_custom_query_struct = generate_custom_query_struct(query_object_option);
158 let generated_merged_query_object_names = generate_merged_query_object_names(query_object_option);
159
160 let generated_custom_mutation_struct = generate_custom_mutation_struct(mutation_object_option);
161 let generated_merged_mutation_object_names = generate_merged_mutation_object_names(mutation_object_option);
162
163 let object_types = all_object_types.into_iter().filter(|object_type| {
164 return
165 object_type.name != "SudographSettings" &&
166 object_type.name != "Query" &&
167 object_type.name != "Mutation";
168 }).collect();
169
170 let generated_object_type_structs = generate_object_type_structs(
171 &graphql_ast,
172 &object_types
173 );
174
175 let generated_create_input_structs = generate_create_input_rust_structs(
176 &graphql_ast,
177 &object_types
178 );
179
180 let generated_read_input_structs = generate_read_input_rust_structs(
181 &graphql_ast,
182 &object_types
183 );
184
185 let read_boolean_input_rust_struct = get_read_boolean_input_rust_struct();
186 let read_date_input_rust_struct = get_read_date_input_rust_struct();
187 let read_float_input_rust_struct = get_read_float_input_rust_struct();
188 let read_id_input_rust_struct = get_read_id_input_rust_struct();
189 let read_int_input_rust_struct = get_read_int_input_rust_struct();
190 let read_string_input_rust_struct = get_read_string_input_rust_struct();
191 let read_enum_input_rust_struct = get_read_enum_input_rust_struct();
192 let read_relation_input_rust_struct = get_read_relation_input_rust_struct();
193 let read_json_input_rust_struct = get_read_json_input_rust_struct();
194 let read_blob_input_rust_struct = get_read_blob_input_rust_struct();
195
196 let generated_order_input_structs = generate_order_input_rust_structs(
197 &graphql_ast,
198 &object_types
199 );
200
201 let generated_update_input_structs = generate_update_input_rust_structs(
202 &graphql_ast,
203 &object_types
204 );
205
206 let generated_delete_input_structs = generate_delete_input_rust_structs(&object_types);
207
208 let generated_upsert_input_structs = generate_upsert_input_rust_structs(
209 &graphql_ast,
210 &object_types
211 );
212
213 let enum_types = get_enum_types(&graphql_ast);
214
215 let generated_enums = generate_enums(&enum_types);
216
217 let generated_query_resolvers = generate_read_query_resolvers(&object_types);
218
219 let generated_create_mutation_resolvers = generate_create_mutation_resolvers(&object_types);
220 let generated_update_mutation_resolvers = generate_update_mutation_resolvers(&object_types);
221 let generated_delete_mutation_resolvers = generate_delete_mutation_resolvers(&object_types);
222
223 let generated_upsert_mutation_resolvers = generate_upsert_mutation_resolvers(
224 &graphql_ast,
225 &object_types
226 );
227
228 let generated_init_mutation_resolvers = generate_init_mutation_resolvers(
229 &graphql_ast,
230 &object_types
231 );
232
233 let generated_init_mutations = object_types.iter().fold(String::from(""), |result, object_type| {
234 let object_type_name = &object_type.name;
235
236 let init_function_name = String::from("init") + object_type_name;
237
238 return result + &init_function_name + "\n";
239 });
240
241 let gen = quote! {
242 use sudograph::serde::{
243 Deserialize,
244 Serialize,
245 self
246 };
247 use sudograph::async_graphql;
248 use sudograph::async_graphql::{
249 SimpleObject,
250 InputObject,
251 Object,
252 MaybeUndefined,
253 Schema,
254 EmptySubscription,
255 scalar,
256 Variables,
257 Request,
258 Enum,
259 MergedObject,
260 Scalar
261 };
262 use sudograph::sudodb::{
263 ObjectTypeStore,
264 create,
265 read,
266 update,
267 delete,
268 init_object_type,
269 FieldTypeInput,
270 FieldType,
271 FieldInput,
272 FieldValue,
273 FieldValueScalar,
274 FieldValueRelationMany,
275 FieldValueRelationOne,
276 ReadInput,
277 ReadInputType,
278 ReadInputOperation,
279 FieldTypeRelationInfo,
280 SelectionSet,
281 SelectionSetInfo,
282 OrderInput,
283 UpdateOperation
284 };
285 use sudograph::serde_json::from_str;
286 use sudograph::ic_cdk;
287 use sudograph::ic_cdk::storage;
288 use sudograph::to_json_string;
289 use sudograph::ic_print;
290 use sudograph::ic_cdk_macros::{
291 query,
292 update,
293 init,
294 post_upgrade,
295 import
296 };
297 use std::error::Error;
298 use std::collections::{
299 BTreeMap,
300 HashMap
301 };
302 use sudograph::rand::{
303 Rng,
304 SeedableRng,
305 rngs::StdRng
306 };
307 type RandStore = BTreeMap<String, StdRng>;
319
320 const temp: &str = include_str!(#schema_absolute_file_path_string);
321
322 #[derive(
326 Serialize,
327 Deserialize,
328 Default,
329 Clone,
330 Debug,
331 )]
333 #[serde(crate="self::serde")]
335 struct ID(String);
336
337 impl ID {
338 fn to_string(&self) -> String {
339 return String::from(&self.0);
340 }
341 }
342
343 scalar!(ID);
344
345 #[derive(
346 Serialize,
347 Deserialize,
348 Default,
349 Clone,
350 Debug,
351 )]
353 #[serde(crate="self::serde")]
355 struct Date(String);
356
357 scalar!(Date);
358
359 #[derive(
360 Serialize,
361 Deserialize,
362 Default,
363 Clone,
364 Debug,
365 )]
367 #[serde(crate="self::serde")]
369 struct Blob(Vec<u8>);
370
371 #[Scalar]
372 impl sudograph::async_graphql::ScalarType for Blob {
373 fn parse(value: sudograph::async_graphql::Value) -> sudograph::async_graphql::InputValueResult<Self> {
374 match value {
375 sudograph::async_graphql::Value::String(value_string) => {
376 return Ok(Blob(value_string.into_bytes()));
377 },
378 sudograph::async_graphql::Value::List(value_list) => {
379 return Ok(Blob(value_list.iter().map(|item| {
380 match item {
381 sudograph::async_graphql::Value::Number(item_number) => {
385 return item_number.as_u64().expect("should be a u64") as u8; },
387 _ => panic!("incorrect value") };
389 }).collect()));
390 },
391 _ => panic!("incorrect value") };
393 }
394
395 fn to_value(&self) -> sudograph::async_graphql::Value {
396 return sudograph::async_graphql::Value::List((&self.0).iter().map(|item_u8| {
397 return sudograph::async_graphql::Value::Number(sudograph::async_graphql::Number::from_f64(*item_u8 as f64).expect("should be able to convert to f64"));
398 }).collect());
399 }
400 }
401
402 #[derive(InputObject, Default, Debug)]
405 struct CreateRelationManyInput {
406 connect: Vec<ID>
407 }
408
409 #[derive(InputObject, Default, Debug)]
410 struct CreateRelationOneInput {
411 connect: ID
412 }
413
414 #[derive(InputObject)]
415 struct UpdateRelationManyInput {
416 connect: Option<Vec<ID>>,
417 disconnect: Option<Vec<ID>>
418 }
419
420 #[derive(InputObject)]
421 struct UpdateNullableRelationOneInput {
422 connect: Option<ID>,
423 disconnect: Option<bool>
424 }
425
426 #[derive(InputObject)]
427 struct UpdateNonNullableRelationOneInput {
428 connect: ID
429 }
430
431 #[derive(Enum, Copy, Clone, Eq, PartialEq)]
432 enum OrderDirection {
433 ASC,
434 DESC
435 }
436
437 #[derive(InputObject)]
438 struct UpdateBlobInput {
439 replace: MaybeUndefined<Blob>,
440 append: Option<Blob>
441 }
443
444 #read_boolean_input_rust_struct
445 #read_date_input_rust_struct
446 #read_float_input_rust_struct
447 #read_id_input_rust_struct
448 #read_int_input_rust_struct
449 #read_string_input_rust_struct
450 #read_enum_input_rust_struct
451 #read_relation_input_rust_struct
452 #read_json_input_rust_struct
453 #read_blob_input_rust_struct
454
455 #(#generated_object_type_structs)*
456 #(#generated_create_input_structs)*
457 #(#generated_read_input_structs)*
458 #(#generated_order_input_structs)*
459 #(#generated_update_input_structs)*
460 #(#generated_delete_input_structs)*
461 #(#generated_enums)*
464
465 trait SudoSerialize {
467 fn sudo_serialize(&self) -> FieldValue;
468 }
469
470 impl SudoSerialize for bool {
471 fn sudo_serialize(&self) -> FieldValue {
472 return FieldValue::Scalar(Some(FieldValueScalar::Boolean(self.clone())));
473 }
474 }
475
476 impl SudoSerialize for f32 {
477 fn sudo_serialize(&self) -> FieldValue {
478 return FieldValue::Scalar(Some(FieldValueScalar::Float(self.clone())));
479 }
480 }
481
482 impl SudoSerialize for ID {
483 fn sudo_serialize(&self) -> FieldValue {
484 return FieldValue::Scalar(Some(FieldValueScalar::String(self.to_string())));
486 }
487 }
488
489 impl SudoSerialize for Date {
490 fn sudo_serialize(&self) -> FieldValue {
491 return FieldValue::Scalar(Some(FieldValueScalar::Date(String::from(&self.0))));
492 }
493 }
494
495 impl SudoSerialize for i32 {
496 fn sudo_serialize(&self) -> FieldValue {
497 return FieldValue::Scalar(Some(FieldValueScalar::Int(self.clone())));
498 }
499 }
500
501 impl SudoSerialize for String {
502 fn sudo_serialize(&self) -> FieldValue {
503 return FieldValue::Scalar(Some(FieldValueScalar::String(self.clone())));
504 }
505 }
506
507 impl SudoSerialize for sudograph::serde_json::Value {
508 fn sudo_serialize(&self) -> FieldValue {
509 return FieldValue::Scalar(Some(FieldValueScalar::JSON(self.to_string())));
510 }
511 }
512
513 impl SudoSerialize for Blob {
514 fn sudo_serialize(&self) -> FieldValue {
515 return FieldValue::Scalar(Some(FieldValueScalar::Blob((&self.0).to_vec())));
516 }
517 }
518
519 impl<T: SudoSerialize> SudoSerialize for Option<T> {
520 fn sudo_serialize(&self) -> FieldValue {
521 match self {
522 Some(value) => {
523 return value.sudo_serialize();
524 },
525 None => {
526 return FieldValue::Scalar(None);
527 }
528 }
529 }
530 }
531
532 #[derive(Default)]
536 pub struct GeneratedQuery;
537
538 #[Object]
539 impl GeneratedQuery {
540 #(#generated_query_resolvers)*
541 }
542
543 #generated_custom_query_struct
544
545 #[derive(MergedObject, Default)]
546 struct Query(
547 #(#generated_merged_query_object_names),*
548 );
549
550 #[derive(Default)]
551 pub struct GeneratedMutation;
552
553 #[Object]
554 impl GeneratedMutation {
555 #(#generated_create_mutation_resolvers)*
556 #(#generated_update_mutation_resolvers)*
557 #(#generated_delete_mutation_resolvers)*
558 #(#generated_init_mutation_resolvers)*
560 #clear_mutation
561
562 }
573
574 #generated_custom_mutation_struct
575
576 #[derive(MergedObject, Default)]
577 struct Mutation(
578 #(#generated_merged_mutation_object_names),*
579 );
580
581 #export_generated_query_function_attribute
582 async fn graphql_query(query_string: String, variables_json_string: String) -> String {
583
584 let schema = Schema::new(
591 Query::default(),
592 Mutation::default(),
593 EmptySubscription
594 );
595
596 let request = Request::new(query_string).variables(Variables::from_json(sudograph::serde_json::from_str(&variables_json_string).expect("This should work")));
606
607 let response = schema.execute(request).await;
608
609 let json_result = to_json_string(&response);
610
611 return json_result.expect("This should work");
612 }
613
614 #export_generated_mutation_function_attribute
615 async fn graphql_mutation(mutation_string: String, variables_json_string: String) -> String {
616 let rand_store = storage::get_mut::<RandStore>();
621
622 let rng_option = rand_store.get("RNG");
623
624 if rng_option.is_none() {
625 let call_result: Result<(Vec<u8>,), _> = ic_cdk::api::call::call(ic_cdk::export::Principal::management_canister(), "raw_rand", ()).await;
631
632 if let Ok(result) = call_result {
633 let rand_store = storage::get_mut::<RandStore>();
634
635 let randomness = result.0;
636
637 let mut rng: StdRng = SeedableRng::from_seed(randomness_vector_to_array(randomness));
638
639 rand_store.insert(String::from("RNG"), rng);
640 }
641 }
642
643 let schema = Schema::new(
645 Query::default(),
646 Mutation::default(),
647 EmptySubscription
648 );
649
650 ic_print("graphql_mutation");
651
652 let request = Request::new(mutation_string).variables(Variables::from_json(sudograph::serde_json::from_str(&variables_json_string).expect("This should work")));
653
654 let response = schema.execute(request).await;
655
656 let json_result = to_json_string(&response).expect("This should work");
660
661 return json_result;
665 }
666
667 #export_generated_init_function_attribute
668 async fn init() {
669 initialize_database_entities().await;
670 }
671
672 #export_generated_post_upgrade_function_attribute
673 async fn post_upgrade() {
674 initialize_database_entities().await;
675 }
676
677 async fn initialize_database_entities() {
678 let schema = Schema::new(
679 Query::default(),
680 Mutation::default(),
681 EmptySubscription
682 );
683
684 let response = schema.execute(format!("
685 mutation {{
686 {generated_init_mutations}
687 }}
688 ",
689 generated_init_mutations = #generated_init_mutations
690 )).await;
691
692 if response.errors.len() > 0 {
694 panic!("{:?}", response.errors);
695 }
696 }
697
698 fn randomness_vector_to_array(randomness: Vec<u8>) -> [u8; 32] {
701 let mut array = [0u8; 32];
702
703 for i in 0..randomness.len() {
704 array[i] = randomness[i];
709 }
710
711 return array;
712 }
713
714 fn convert_selection_field_to_selection_set(
715 object_type_name: &str,
716 selection_field: sudograph::async_graphql::context::SelectionField<'_>,
717 selection_set: SelectionSet
718 ) -> SelectionSet {
719 let selection_fields: Vec<sudograph::async_graphql::context::SelectionField<'_>> = selection_field.selection_set().collect();
720
721 if selection_fields.len() == 0 {
722 return selection_set;
723 }
724
725 let graphql_ast = sudograph::graphql_parser::schema::parse_schema::<String>(#schema_file_contents).unwrap();
729
730 let mut hash_map = HashMap::new();
731
732 for selection_field in selection_fields {
733 let child_type_name = get_type_name_for_object_type_name_and_field_name(
736 &graphql_ast,
737 object_type_name,
738 selection_field.name()
739 );
740
741 let child_selection_set = convert_selection_field_to_selection_set(
742 &child_type_name,
743 selection_field,
744 SelectionSet(None)
745 );
746
747 let child_selection_set_info = SelectionSetInfo {
748 selection_set: child_selection_set,
749 search_inputs: get_search_inputs_from_selection_field(
750 &graphql_ast,
751 object_type_name,
752 selection_field
753 ),
754 limit_option: get_limit_option_from_selection_field(selection_field),
755 offset_option: get_offset_option_from_selection_field(selection_field),
756 order_inputs: get_order_inputs_from_selection_field(selection_field)
757 };
758
759 hash_map.insert(String::from(selection_field.name()), child_selection_set_info);
760 }
761
762 return SelectionSet(Some(hash_map));
763 }
764
765 fn get_search_inputs_from_selection_field(
766 graphql_ast: &sudograph::graphql_parser::schema::Document<String>,
767 object_type_name: &str,
768 selection_field: sudograph::async_graphql::context::SelectionField<'_>
769 ) -> Vec<ReadInput> {
770 match selection_field.arguments() {
771 Ok(arguments) => {
772 let search_argument_option = arguments.iter().find(|argument| {
773 return argument.0.as_str() == "search";
774 });
775
776 match search_argument_option {
777 Some(search_argument) => {
778 let relation_object_type_name = get_type_name_for_object_type_name_and_field_name(
779 graphql_ast,
780 object_type_name,
781 selection_field.name()
782 );
783
784 return get_search_inputs_from_value(
785 graphql_ast,
786 &relation_object_type_name,
787 &search_argument.1
788 )
789 .into_iter()
790 .flatten()
791 .collect();
792 },
793 None => {
794 return vec![];
795 }
796 };
797 },
798 _ => {
799 return vec![];
801 }
802 };
803 }
804
805 fn get_search_inputs_from_value(
807 graphql_ast: &sudograph::graphql_parser::schema::Document<String>,
808 object_type_name: &str,
809 value: &sudograph::async_graphql::Value
810 ) -> Vec<Vec<ReadInput>> {
811 match value {
812 sudograph::async_graphql::Value::Object(object) => {
813 let search_inputs = object.keys().fold(vec![], |mut result, object_key| {
814 let object_value = object.get(object_key).expect("get_search_inputs_from_value::object_value");
815
816 if object_key == "and" {
817 result.push(vec![ReadInput {
818 input_type: ReadInputType::Scalar,
819 input_operation: ReadInputOperation::Equals,
820 field_name: String::from("and"),
821 field_value: FieldValue::Scalar(None),
822 relation_object_type_name: String::from(""),
823 relation_read_inputs: vec![],
824 and: match object_value {
825 sudograph::async_graphql::Value::List(list) => list.iter().flat_map(|value| { get_search_inputs_from_value(
826 graphql_ast,
827 object_type_name,
828 value
829 )
830 .into_iter()
831 .flatten()
832 .collect::<Vec<ReadInput>>() }).collect(),
833 sudograph::async_graphql::Value::Object(_) => {
834 get_search_inputs_from_value(
835 graphql_ast,
836 object_type_name,
837 object_value
838 )
839 .into_iter()
840 .flatten()
841 .collect()
842 },
843 _ => panic!("panic for and")
844 },
845 or: vec![]
846 }]);
847
848 return result;
849 }
850
851 if object_key == "or" {
852 match object_value {
853 sudograph::async_graphql::Value::List(list) => {
854 for value in list {
855 result.push(
856 vec![
857 ReadInput {
858 input_type: ReadInputType::Scalar,
859 input_operation: ReadInputOperation::Equals,
860 field_name: String::from("or"),
861 field_value: FieldValue::Scalar(None),
862 relation_object_type_name: String::from(""),
863 relation_read_inputs: vec![],
864 and: vec![],
865 or: get_search_inputs_from_value(
866 graphql_ast,
867 object_type_name,
868 value
869 )
870 .into_iter()
871 .map(|read_inputs| {
872 return ReadInput {
873 input_type: ReadInputType::Scalar,
874 input_operation: ReadInputOperation::Equals,
875 field_name: String::from("and"),
876 field_value: FieldValue::Scalar(None),
877 relation_object_type_name: String::from(""),
878 relation_read_inputs: vec![],
879 and: read_inputs,
880 or: vec![]
881 };
882 })
883 .collect()
884 }
885 ]
886 );
887 }
888 },
889 sudograph::async_graphql::Value::Object(_) => {
890 result.push(
891 vec![
892 ReadInput {
893 input_type: ReadInputType::Scalar,
894 input_operation: ReadInputOperation::Equals,
895 field_name: String::from("or"),
896 field_value: FieldValue::Scalar(None),
897 relation_object_type_name: String::from(""),
898 relation_read_inputs: vec![],
899 and: vec![],
900 or: get_search_inputs_from_value(
901 graphql_ast,
902 object_type_name,
903 object_value
904 )
905 .into_iter()
906 .map(|read_inputs| {
907 return ReadInput {
908 input_type: ReadInputType::Scalar,
909 input_operation: ReadInputOperation::Equals,
910 field_name: String::from("and"),
911 field_value: FieldValue::Scalar(None),
912 relation_object_type_name: String::from(""),
913 relation_read_inputs: vec![],
914 and: read_inputs,
915 or: vec![]
916 };
917 })
918 .collect()
919 }
920 ]
921 );
922 },
923 _ => panic!()
924 };
925
926
927 return result;
928 }
929
930 let field = get_field_for_object_type_name_and_field_name(
931 graphql_ast,
932 object_type_name,
933 object_key
934 );
935
936 if
937 is_graphql_type_a_relation_many(
938 graphql_ast,
939 &field.field_type
940 ) == true ||
941 is_graphql_type_a_relation_one(
942 graphql_ast,
943 &field.field_type
944 ) == true
945 {
946 let relation_object_type_name = get_field_type_name(&field);
947
948 result.push(vec![ReadInput {
949 input_type: ReadInputType::Relation,
950 input_operation: ReadInputOperation::Equals,
951 field_name: object_key.to_string(),
952 field_value: FieldValue::Scalar(None),
953 relation_object_type_name: String::from(&relation_object_type_name),
954 relation_read_inputs: get_search_inputs_from_value(
955 graphql_ast,
956 &relation_object_type_name,
957 object_value
958 )
959 .into_iter()
960 .flatten()
961 .collect(),
962 and: vec![],
963 or: vec![]
964 }]);
965
966 return result;
967 }
968 else {
969 match object_value {
970 sudograph::async_graphql::Value::Object(scalar_object) => {
971 let scalar_search_inputs: Vec<ReadInput> = scalar_object.keys().map(|scalar_object_key| {
972 let scalar_object_value = scalar_object.get(scalar_object_key).unwrap();
973
974 let input_operation = match scalar_object_key.as_str() {
975 "eq" => ReadInputOperation::Equals,
976 "gt" => ReadInputOperation::GreaterThan,
977 "gte" => ReadInputOperation::GreaterThanOrEqualTo,
978 "lt" => ReadInputOperation::LessThan,
979 "lte" => ReadInputOperation::LessThanOrEqualTo,
980 "contains" => ReadInputOperation::Contains,
981 "startsWith" => ReadInputOperation::StartsWith,
982 "endsWith" => ReadInputOperation::EndsWith,
983 _ => panic!()
984 };
985
986 let graphql_type_name = get_graphql_type_name(&field.field_type);
987
988 let field_value = match graphql_type_name.as_str() {
990 "Blob" => match scalar_object_value {
991 sudograph::async_graphql::Value::String(value_string) => FieldValue::Scalar(Some(FieldValueScalar::Blob(value_string.clone().into_bytes()))),
992 sudograph::async_graphql::Value::List(value_list) => FieldValue::Scalar(Some(FieldValueScalar::Blob(value_list.iter().map(|item| {
993 match item {
994 sudograph::async_graphql::Value::Number(item_number) => {
998 return item_number.as_u64().expect("should be a u64") as u8; },
1000 _ => panic!("incorrect value") };
1002 }).collect()))),
1003 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1004 _ => panic!() },
1006 "Boolean" => match scalar_object_value {
1007 sudograph::async_graphql::Value::Boolean(boolean) => FieldValue::Scalar(Some(FieldValueScalar::Boolean(boolean.clone()))),
1008 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1009 _ => panic!()
1010 },
1011 "Date" => match scalar_object_value {
1012 sudograph::async_graphql::Value::String(date_string) => FieldValue::Scalar(Some(FieldValueScalar::Date(date_string.to_string()))),
1013 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1014 _ => panic!()
1015 },
1016 "Float" => match scalar_object_value {
1017 sudograph::async_graphql::Value::Number(number) => FieldValue::Scalar(Some(FieldValueScalar::Float(number.as_f64().unwrap() as f32))),
1018 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1019 _ => panic!()
1020 },
1021 "ID" => match scalar_object_value {
1022 sudograph::async_graphql::Value::String(id_string) => FieldValue::Scalar(Some(FieldValueScalar::String(id_string.to_string()))),
1023 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1024 _ => panic!()
1025 },
1026 "Int" => match scalar_object_value {
1027 sudograph::async_graphql::Value::Number(number) => FieldValue::Scalar(Some(FieldValueScalar::Int(number.as_i64().unwrap() as i32))),
1028 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1029 _ => panic!()
1030 },
1031 "JSON" => match scalar_object_value {
1032 sudograph::async_graphql::Value::String(string) => FieldValue::Scalar(Some(FieldValueScalar::JSON(string.to_string()))),
1033 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1034 _ => panic!()
1035 },
1036 "String" => match scalar_object_value {
1037 sudograph::async_graphql::Value::String(string) => FieldValue::Scalar(Some(FieldValueScalar::String(string.to_string()))),
1038 sudograph::async_graphql::Value::Null => FieldValue::Scalar(None),
1039 _ => panic!()
1040 },
1041 _ => panic!("this scalar is not defined")
1042 };
1043
1044 return ReadInput {
1045 input_type: ReadInputType::Scalar,
1046 input_operation: input_operation,
1047 field_name: object_key.to_string(),
1048 field_value,
1049 relation_object_type_name: String::from(""),
1050 relation_read_inputs: vec![],
1051 and: vec![],
1052 or: vec![]
1053 };
1054 }).collect();
1055
1056 result.push(scalar_search_inputs);
1057
1058 return result;
1059 },
1060 _ => {
1061 panic!();
1062 }
1063 };
1064 }
1065 });
1066
1067 return search_inputs;
1068 },
1069 _ => {
1070 panic!(); }
1072 }
1073 }
1074
1075 fn get_type_name_for_object_type_name_and_field_name(
1076 graphql_ast: &sudograph::graphql_parser::schema::Document<String>,
1077 object_type_name: &str,
1078 field_name: &str
1079 ) -> String {
1080 let object_type = get_object_type(
1081 graphql_ast,
1082 object_type_name
1083 );
1084 let field = get_field(
1085 &object_type,
1086 field_name
1087 );
1088 let field_type_name = get_field_type_name(&field);
1089
1090 return field_type_name;
1091 }
1092
1093 fn get_field_for_object_type_name_and_field_name<'a>(
1094 graphql_ast: &sudograph::graphql_parser::schema::Document<'a, String>,
1095 object_type_name: &str,
1096 field_name: &str
1097 ) -> sudograph::graphql_parser::schema::Field<'a, String> {
1098 let object_type = get_object_type(
1099 graphql_ast,
1100 object_type_name
1101 );
1102 let field = get_field(
1103 &object_type,
1104 field_name
1105 );
1106
1107 return field;
1108 }
1109
1110 fn get_object_types<'a>(graphql_ast: &sudograph::graphql_parser::schema::Document<'a, String>) -> Vec<sudograph::graphql_parser::schema::ObjectType<'a, String>> {
1111 let type_definitions: Vec<sudograph::graphql_parser::schema::TypeDefinition<String>> = graphql_ast.definitions.iter().filter_map(|definition| {
1112 match definition {
1113 sudograph::graphql_parser::schema::Definition::TypeDefinition(type_definition) => {
1114 return Some(type_definition.clone());
1115 },
1116 _ => {
1117 return None;
1118 }
1119 };
1120 }).collect();
1121
1122 let object_types = type_definitions.into_iter().filter_map(|type_definition| {
1123 match type_definition {
1124 sudograph::graphql_parser::schema::TypeDefinition::Object(object_type) => {
1125 return Some(object_type);
1126 },
1127 _ => {
1128 return None;
1129 }
1130 }
1131 }).collect();
1132
1133 return object_types;
1134 }
1135
1136 fn get_object_type<'a>(
1137 graphql_ast: &sudograph::graphql_parser::schema::Document<'a, String>,
1138 object_type_name: &str
1139 ) -> sudograph::graphql_parser::schema::ObjectType<'a, String> {
1140 let object_types = get_object_types(graphql_ast);
1141 let object_type = object_types.iter().find(|object_type| {
1142 return object_type.name == object_type_name;
1143 }).expect("get_object_type::object_type");
1144
1145 return object_type.clone();
1146 }
1147
1148 fn get_field<'a>(
1149 object_type: &sudograph::graphql_parser::schema::ObjectType<'a, String>,
1150 field_name: &str
1151 ) -> sudograph::graphql_parser::schema::Field<'a, String> {
1152 return object_type.fields.iter().find(|field| {
1155 return field.name == field_name;
1156 }).expect("get_field").clone(); }
1158
1159 fn get_field_type_name(
1160 field: &sudograph::graphql_parser::schema::Field<String>
1161 ) -> String {
1162 return get_graphql_type_name(&field.field_type);
1163 }
1164
1165 fn get_graphql_type_name(graphql_type: &sudograph::graphql_parser::schema::Type<String>) -> String {
1167 match graphql_type {
1168 sudograph::graphql_parser::schema::Type::NamedType(named_type) => {
1169 return String::from(named_type);
1170 },
1171 sudograph::graphql_parser::schema::Type::NonNullType(non_null_type) => {
1172 return get_graphql_type_name(non_null_type);
1173 },
1174 sudograph::graphql_parser::schema::Type::ListType(list_type) => {
1175 return get_graphql_type_name(list_type);
1176 }
1177 };
1178 }
1179
1180 fn get_limit_option_from_selection_field(selection_field: sudograph::async_graphql::context::SelectionField<'_>) -> Option<u32> {
1181 match selection_field.arguments() {
1182 Ok(arguments) => {
1183 let limit_argument_option = arguments.iter().find(|argument| {
1184 return argument.0.as_str() == "limit";
1185 });
1186
1187 match limit_argument_option {
1188 Some(limit_argument) => {
1189 match &limit_argument.1 {
1190 sudograph::async_graphql::Value::Number(number) => {
1191 match number.as_u64() {
1192 Some(number_u64) => {
1193 return Some(number_u64 as u32);
1194 },
1195 None => {
1196 return None;
1197 }
1198 };
1199 },
1200 _ => {
1201 return None; }
1203 };
1204 },
1205 None => {
1206 return None;
1207 }
1208 };
1209 },
1210 _ => {
1211 return None;
1214 }
1215 };
1216 }
1217
1218 fn get_offset_option_from_selection_field(selection_field: sudograph::async_graphql::context::SelectionField<'_>) -> Option<u32> {
1219 match selection_field.arguments() {
1220 Ok(arguments) => {
1221 let limit_argument_option = arguments.iter().find(|argument| {
1222 return argument.0.as_str() == "offset";
1223 });
1224
1225 match limit_argument_option {
1226 Some(limit_argument) => {
1227 match &limit_argument.1 {
1228 sudograph::async_graphql::Value::Number(number) => {
1229 match number.as_u64() {
1230 Some(number_u64) => {
1231 return Some(number_u64 as u32);
1232 },
1233 None => {
1234 return None;
1235 }
1236 };
1237 },
1238 _ => {
1239 return None; }
1241 };
1242 },
1243 None => {
1244 return None;
1245 }
1246 };
1247 },
1248 _ => {
1249 return None;
1252 }
1253 };
1254 }
1255
1256 fn get_order_inputs_from_selection_field(selection_field: sudograph::async_graphql::context::SelectionField<'_>) -> Vec<sudograph::sudodb::OrderInput> {
1257 match selection_field.arguments() {
1258 Ok(arguments) => {
1259 let order_argument_option = arguments.iter().find(|argument| {
1260 return argument.0.as_str() == "order";
1261 });
1262
1263 match order_argument_option {
1264 Some(order_argument) => {
1265 match &order_argument.1 {
1266 sudograph::async_graphql::Value::Object(object) => {
1267 return object.keys().map(|key| {
1268 let value = object.get(key).expect("get_order_inputs_from_selection_field::value"); return sudograph::sudodb::OrderInput {
1271 field_name: String::from(key.as_str()),
1272 order_direction: match value {
1273 sudograph::async_graphql::Value::Enum(name) => {
1274 if name.as_str() == "ASC" {
1275 sudograph::sudodb::OrderDirection::ASC
1276 }
1277 else {
1279 sudograph::sudodb::OrderDirection::DESC
1280 }
1281 },
1282 _ => panic!("bad")
1283 }
1284 };
1285 }).collect();
1286 },
1287 _ => {
1288 return vec![]; }
1290 };
1291 },
1292 None => {
1293 return vec![];
1294 }
1295 };
1296 },
1297 _ => {
1298 return vec![];
1300 }
1301 };
1302 }
1303
1304 fn get_field_arguments(
1305 context: &sudograph::async_graphql::Context<'_>,
1306 field_name: &str
1307 ) -> sudograph::async_graphql::ServerResult<Vec<(sudograph::async_graphql::Name, sudograph::async_graphql::Value)>> {
1308 let selection_field_option = context.field().selection_set().find(|selection_field| {
1309 return selection_field.name() == field_name;
1310 });
1311
1312 match selection_field_option {
1313 Some(selection_field) => {
1314 return selection_field.arguments();
1315 },
1316 None => {
1317 return Ok(vec![]);
1318 }
1319 };
1320 }
1321
1322 fn is_graphql_type_a_relation_many(
1323 graphql_ast: &sudograph::graphql_parser::schema::Document<String>,
1324 graphql_type: &sudograph::graphql_parser::schema::Type<String>
1325 ) -> bool {
1326 let object_types = get_object_types(graphql_ast);
1327 let graphql_type_name = get_graphql_type_name(graphql_type);
1328
1329 let graphql_type_is_a_relation = object_types.iter().any(|object_type| {
1330 return object_type.name == graphql_type_name;
1331 });
1332
1333 let graphql_type_is_a_list_type = is_graphql_type_a_list_type(
1334 graphql_ast,
1335 graphql_type
1336 );
1337
1338 return
1339 graphql_type_is_a_relation == true &&
1340 graphql_type_is_a_list_type == true
1341 ;
1342 }
1343
1344 fn is_graphql_type_a_relation_one(
1345 graphql_ast: &sudograph::graphql_parser::schema::Document<String>,
1346 graphql_type: &sudograph::graphql_parser::schema::Type<String>
1347 ) -> bool {
1348 let object_types = get_object_types(graphql_ast);
1349 let graphql_type_name = get_graphql_type_name(graphql_type);
1350
1351 let graphql_type_is_a_relation = object_types.iter().any(|object_type| {
1352 return object_type.name == graphql_type_name;
1353 });
1354
1355 let graphql_type_is_a_list_type = is_graphql_type_a_list_type(
1356 graphql_ast,
1357 graphql_type
1358 );
1359
1360 return
1361 graphql_type_is_a_relation == true &&
1362 graphql_type_is_a_list_type == false
1363 ;
1364 }
1365
1366 fn is_graphql_type_a_list_type(
1367 graphql_ast: &sudograph::graphql_parser::schema::Document<String>,
1368 graphql_type: &sudograph::graphql_parser::schema::Type<String>
1369 ) -> bool {
1370 match graphql_type {
1371 sudograph::graphql_parser::schema::Type::NamedType(_) => {
1372 return false;
1373 },
1374 sudograph::graphql_parser::schema::Type::NonNullType(non_null_type) => {
1375 return is_graphql_type_a_list_type(
1376 graphql_ast,
1377 non_null_type
1378 );
1379 },
1380 sudograph::graphql_parser::schema::Type::ListType(_) => {
1381 return true;
1382 }
1383 };
1384 }
1385 };
1386
1387 return gen.into();
1388}
1389
1390fn get_graphql_type_name(graphql_type: &Type<String>) -> String {
1393 match graphql_type {
1394 Type::NamedType(named_type) => {
1395 return String::from(named_type);
1396 },
1397 Type::NonNullType(non_null_type) => {
1398 return get_graphql_type_name(non_null_type);
1399 },
1400 Type::ListType(list_type) => {
1401 return get_graphql_type_name(list_type);
1402 }
1403 };
1404}
1405
1406fn is_graphql_type_nullable(graphql_type: &Type<String>) -> bool {
1407 match graphql_type {
1408 Type::NonNullType(_) => {
1409 return false;
1410 },
1411 _ => {
1412 return true;
1413 }
1414 };
1415}
1416
1417fn is_field_a_relation(
1418 graphql_ast: &Document<String>,
1419 field: &Field<String>
1420) -> bool {
1421 return
1422 is_graphql_type_a_relation_many(
1423 graphql_ast,
1424 &field.field_type
1425 ) == true ||
1426 is_graphql_type_a_relation_one(
1427 graphql_ast,
1428 &field.field_type
1429 ) == true;
1430}
1431
1432fn is_graphql_type_a_relation_many(
1433 graphql_ast: &Document<String>,
1434 graphql_type: &Type<String>
1435) -> bool {
1436 let object_types = get_object_types(graphql_ast);
1437 let graphql_type_name = get_graphql_type_name(graphql_type);
1438
1439 let graphql_type_is_a_relation = object_types.iter().any(|object_type| {
1440 return object_type.name == graphql_type_name;
1441 });
1442
1443 let graphql_type_is_a_list_type = is_graphql_type_a_list_type(
1444 graphql_ast,
1445 graphql_type
1446 );
1447
1448 return
1449 graphql_type_is_a_relation == true &&
1450 graphql_type_is_a_list_type == true
1451 ;
1452}
1453
1454fn is_graphql_type_a_relation_one(
1455 graphql_ast: &Document<String>,
1456 graphql_type: &Type<String>
1457) -> bool {
1458 let object_types = get_object_types(graphql_ast);
1459 let graphql_type_name = get_graphql_type_name(graphql_type);
1460
1461 let graphql_type_is_a_relation = object_types.iter().any(|object_type| {
1462 return object_type.name == graphql_type_name;
1463 });
1464
1465 let graphql_type_is_a_list_type = is_graphql_type_a_list_type(
1466 graphql_ast,
1467 graphql_type
1468 );
1469
1470 return
1471 graphql_type_is_a_relation == true &&
1472 graphql_type_is_a_list_type == false
1473 ;
1474}
1475
1476fn is_graphql_type_an_enum(
1477 graphql_ast: &Document<String>,
1478 graphql_type: &Type<String>
1479) -> bool {
1480 let enum_types = get_enum_types(graphql_ast);
1481 let graphql_type_name = get_graphql_type_name(graphql_type);
1482
1483 let graphql_type_is_an_enum = enum_types.iter().any(|enum_type| {
1484 return enum_type.name == graphql_type_name;
1485 });
1486
1487 return graphql_type_is_an_enum;
1488}
1489
1490fn is_graphql_type_a_blob(graphql_type: &Type<String>) -> bool {
1491 let graphql_type_name = get_graphql_type_name(graphql_type);
1492
1493 return graphql_type_name == "Blob";
1494}
1495
1496fn is_graphql_type_a_list_type(
1497 graphql_ast: &Document<String>,
1498 graphql_type: &Type<String>
1499) -> bool {
1500 match graphql_type {
1501 Type::NamedType(_) => {
1502 return false;
1503 },
1504 Type::NonNullType(non_null_type) => {
1505 return is_graphql_type_a_list_type(
1506 graphql_ast,
1507 non_null_type
1508 );
1509 },
1510 Type::ListType(_) => {
1511 return true;
1512 }
1513 };
1514}
1515
1516fn get_object_types<'a>(graphql_ast: &Document<'a, String>) -> Vec<ObjectType<'a, String>> {
1517 let type_definitions: Vec<TypeDefinition<String>> = graphql_ast.definitions.iter().filter_map(|definition| {
1518 match definition {
1519 Definition::TypeDefinition(type_definition) => {
1520 return Some(type_definition.clone());
1521 },
1522 _ => {
1523 return None;
1524 }
1525 };
1526 }).collect();
1527
1528 let object_types: Vec<ObjectType<String>> = type_definitions.into_iter().filter_map(|type_definition| {
1529 match type_definition {
1530 TypeDefinition::Object(object_type) => {
1531 return Some(object_type);
1532 },
1533 _ => {
1534 return None;
1535 }
1536 }
1537 }).collect();
1538
1539 return object_types;
1540}
1541
1542fn get_enum_types<'a>(graphql_ast: &Document<'a, String>) -> Vec<EnumType<'a, String>> {
1543 let type_definitions: Vec<TypeDefinition<String>> = graphql_ast.definitions.iter().filter_map(|definition| {
1544 match definition {
1545 Definition::TypeDefinition(type_definition) => {
1546 return Some(type_definition.clone());
1547 },
1548 _ => {
1549 return None;
1550 }
1551 };
1552 }).collect();
1553
1554 let enum_types: Vec<EnumType<String>> = type_definitions.into_iter().filter_map(|type_definition| {
1555 match type_definition {
1556 TypeDefinition::Enum(enum_type) => {
1557 return Some(enum_type);
1558 },
1559 _ => {
1560 return None;
1561 }
1562 }
1563 }).collect();
1564
1565 return enum_types;
1566}
1567
1568fn get_opposing_relation_field<'a>(
1571 graphql_ast: &'a Document<'a, String>,
1572 relation_field: &Field<String>
1573) -> Option<Field<'a, String>> {
1574 let relation_name = get_directive_argument_value_from_field(
1575 relation_field,
1576 "relation",
1577 "name"
1578 )?;
1579
1580 let opposing_object_type_name = get_graphql_type_name(&relation_field.field_type);
1581
1582 let object_types = get_object_types(graphql_ast);
1583
1584 return object_types.iter().filter(|object_type| {
1585 return object_type.name == opposing_object_type_name; }).fold(None, |_, object_type| {
1587 return object_type.fields.iter().fold(None, |result, field| {
1588 if result != None {
1589 return result;
1590 }
1591
1592 let opposing_relation_name = get_directive_argument_value_from_field(
1593 field,
1594 "relation",
1595 "name"
1596 )?;
1597
1598 if opposing_relation_name == relation_name {
1599 return Some(field.clone());
1600 }
1601 else {
1602 return result;
1603 }
1604 });
1605 });
1606}
1607
1608fn get_directive_argument_value_from_field(
1609 field: &Field<String>,
1610 directive_name: &str,
1611 argument_name: &str
1612) -> Option<String> {
1613 let directive = field.directives.iter().find(|directive| {
1614 return directive.name == directive_name;
1615 })?;
1616
1617 let argument = directive.arguments.iter().find(|argument| {
1618 return argument.0 == argument_name;
1619 })?;
1620
1621 return Some(argument.1.to_string());
1622}
1623
1624fn get_object_type_from_field<'a>(
1625 graphql_ast: &Document<'a, String>,
1626 field: &Field<String>
1627) -> Option<ObjectType<'a, String>> {
1628 let object_type_name = get_graphql_type_name(&field.field_type);
1629
1630 let object_types = get_object_types(graphql_ast);
1631
1632 return object_types.into_iter().find(|object_type| {
1633 return object_type.name == object_type_name;
1634 }).clone();
1635}
1636
1637fn get_enum_type_from_field<'a>(
1638 graphql_ast: &Document<'a, String>,
1639 field: &Field<String>
1640) -> Option<EnumType<'a, String>> {
1641 let enum_type_name = get_graphql_type_name(&field.field_type);
1642
1643 let enum_types = get_enum_types(graphql_ast);
1644
1645 return enum_types.into_iter().find(|enum_type| {
1646 return enum_type.name == enum_type_name;
1647 }).clone();
1648}
1649
1650fn get_scalar_fields<'a>(
1651 graphql_ast: &Document<String>,
1652 object_type: &ObjectType<'a, String>
1653) -> Vec<Field<'a, String>> {
1654 return object_type.fields.iter().cloned().filter(|field| {
1655 return
1656 is_graphql_type_a_relation_many(
1657 graphql_ast,
1658 &field.field_type
1659 ) == false &&
1660 is_graphql_type_a_relation_one(
1661 graphql_ast,
1662 &field.field_type
1663 ) == false;
1664 }).collect();
1665}
1666
1667fn get_relation_fields<'a>(
1668 graphql_ast: &Document<String>,
1669 object_type: &ObjectType<'a, String>
1670) -> Vec<Field<'a, String>> {
1671 return object_type.fields.iter().cloned().filter(|field| {
1672 return
1673 is_graphql_type_a_relation_many(
1674 graphql_ast,
1675 &field.field_type
1676 ) == true ||
1677 is_graphql_type_a_relation_one(
1678 graphql_ast,
1679 &field.field_type
1680 ) == true;
1681 }).collect();
1682}