dynamic_graphql/
registry.rs1use std::any::TypeId;
2use std::collections::HashMap;
3use std::collections::HashSet;
4use std::collections::VecDeque;
5use std::mem;
6
7use crate::data::SchemaData;
8use crate::dynamic;
9use crate::types::Register;
10
11pub struct Registry {
12 pub data: SchemaData,
13 root: Option<String>,
14 mutation: Option<String>,
15 subscription: Option<String>,
16 objects: HashMap<String, dynamic::Object>,
17 types: Vec<dynamic::Type>,
18 names: HashSet<TypeId>,
20 pending_expand_objects: VecDeque<PendingExpandObject>,
21}
22
23impl Default for Registry {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl Registry {
30 pub fn new() -> Self {
31 Self {
32 data: Default::default(),
33 root: None,
34 mutation: None,
35 subscription: None,
36 objects: Default::default(),
37 types: Default::default(),
38 names: Default::default(),
39 pending_expand_objects: Default::default(),
40 }
41 }
42}
43
44struct PendingExpandObject {
45 target: String,
46 expansion: String,
47 map_fn: Box<dyn FnOnce(dynamic::Object) -> dynamic::Object>,
48}
49
50impl Registry {
51 #[inline]
52 pub fn set_root(mut self, name: &str) -> Self {
53 self.root = Some(name.to_string());
54 self
55 }
56 #[inline]
57 pub fn set_mutation(mut self, name: &str) -> Self {
58 self.mutation = Some(name.to_string());
59 self
60 }
61 #[inline]
62 pub fn set_subscription(mut self, name: &str) -> Self {
63 self.subscription = Some(name.to_string());
64 self
65 }
66 pub fn register_type(mut self, ty: impl Into<dynamic::Type>) -> Self {
67 let ty = ty.into();
68 match ty {
69 dynamic::Type::Object(object) => {
70 self.objects.insert(object.type_name().to_string(), object);
71 }
72 _ => {
73 self.types.push(ty);
74 }
75 }
76 self
77 }
78 pub fn update_object<F>(mut self, target: &str, expansion_name: &str, f: F) -> Self
79 where
80 F: FnOnce(dynamic::Object) -> dynamic::Object + 'static,
81 {
82 self.pending_expand_objects.push_back(PendingExpandObject {
83 target: target.to_string(),
84 expansion: expansion_name.to_string(),
85 map_fn: Box::new(f),
86 });
87 self
88 }
89}
90
91impl Registry {
92 pub fn register<T: Register + ?Sized + 'static>(mut self) -> Self {
93 let ty = TypeId::of::<T>();
94 if self.names.contains(&ty) {
95 return self;
96 }
97 self.names.insert(ty);
98 T::register(self)
99 }
100
101 fn apply_pending_objects(&mut self) {
102 loop {
103 if self.pending_expand_objects.is_empty() {
104 break;
105 }
106 let mut changed = false;
107 let pending_expand_objects = mem::take(&mut self.pending_expand_objects);
108 self.pending_expand_objects = pending_expand_objects
109 .into_iter()
110 .filter_map(|pending| {
111 if let Some(object) = self.objects.remove(&pending.target) {
112 self.objects
113 .insert(pending.target, (pending.map_fn)(object));
114 changed = true;
115 None
116 } else {
117 Some(pending)
118 }
119 })
120 .collect();
121 if !changed {
122 let keys = self
123 .pending_expand_objects
124 .iter()
125 .map(|p| format!("{} when defining {}", p.target, p.expansion))
126 .collect::<Vec<_>>()
127 .join(", ");
128 panic!("Can't find object: {:?}", keys);
129 }
130 }
131 }
132 pub fn create_schema(self) -> dynamic::SchemaBuilder {
133 let Some(ref root) = self.root else {
134 panic!("No root object defined");
135 };
136 let schema =
137 dynamic::Schema::build(root, self.mutation.as_deref(), self.subscription.as_deref());
138 self.apply_into_schema_builder(schema)
139 }
140
141 pub fn apply_into_schema_builder(
142 mut self,
143 schema: dynamic::SchemaBuilder,
144 ) -> dynamic::SchemaBuilder {
145 self.apply_pending_objects();
146 let schema = self
147 .objects
148 .into_iter()
149 .fold(schema, |schema, (_, object)| schema.register(object));
150 let schema = self
151 .types
152 .into_iter()
153 .fold(schema, |schema, object| schema.register(object));
154 schema.data(self.data)
155 }
156}