dynamic_graphql/
registry.rs

1use 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    // name of all registered types
19    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}