tensorlogic_adapters/codegen/
graphql.rs1use std::fmt::Write as FmtWrite;
2
3use crate::{DomainInfo, PredicateInfo, SymbolTable};
4
5use super::rust::RustCodegen;
6
7pub struct GraphQLCodegen {
12 schema_name: String,
14 include_descriptions: bool,
16 generate_queries: bool,
18 generate_mutations: bool,
20}
21
22impl GraphQLCodegen {
23 pub fn new(schema_name: impl Into<String>) -> Self {
25 Self {
26 schema_name: schema_name.into(),
27 include_descriptions: true,
28 generate_queries: true,
29 generate_mutations: false,
30 }
31 }
32
33 pub fn with_descriptions(mut self, enable: bool) -> Self {
35 self.include_descriptions = enable;
36 self
37 }
38
39 pub fn with_queries(mut self, enable: bool) -> Self {
41 self.generate_queries = enable;
42 self
43 }
44
45 pub fn with_mutations(mut self, enable: bool) -> Self {
47 self.generate_mutations = enable;
48 self
49 }
50
51 pub fn generate(&self, table: &SymbolTable) -> String {
53 let mut schema = String::new();
54
55 writeln!(schema, "# Generated GraphQL Schema").expect("writing to String is infallible");
57 writeln!(schema, "# Schema: {}", self.schema_name)
58 .expect("writing to String is infallible");
59 writeln!(schema, "#").expect("writing to String is infallible");
60 writeln!(
61 schema,
62 "# This schema was automatically generated from TensorLogic."
63 )
64 .expect("writing to String is infallible");
65 writeln!(schema, "# DO NOT EDIT MANUALLY.").expect("writing to String is infallible");
66 writeln!(schema).expect("writing to String is infallible");
67
68 writeln!(schema, "# ==========================================")
70 .expect("writing to String is infallible");
71 writeln!(schema, "# Domain Types").expect("writing to String is infallible");
72 writeln!(schema, "# ==========================================")
73 .expect("writing to String is infallible");
74 writeln!(schema).expect("writing to String is infallible");
75
76 for domain in table.domains.values() {
77 self.generate_domain_type(&mut schema, domain);
78 writeln!(schema).expect("writing to String is infallible");
79 }
80
81 writeln!(schema, "# ==========================================")
83 .expect("writing to String is infallible");
84 writeln!(schema, "# Predicate Types").expect("writing to String is infallible");
85 writeln!(schema, "# ==========================================")
86 .expect("writing to String is infallible");
87 writeln!(schema).expect("writing to String is infallible");
88
89 for predicate in table.predicates.values() {
90 self.generate_predicate_type(&mut schema, predicate, table);
91 writeln!(schema).expect("writing to String is infallible");
92 }
93
94 if self.generate_queries {
96 writeln!(schema, "# ==========================================")
97 .expect("writing to String is infallible");
98 writeln!(schema, "# Query Operations").expect("writing to String is infallible");
99 writeln!(schema, "# ==========================================")
100 .expect("writing to String is infallible");
101 writeln!(schema).expect("writing to String is infallible");
102 self.generate_query_type(&mut schema, table);
103 writeln!(schema).expect("writing to String is infallible");
104 }
105
106 if self.generate_mutations {
108 writeln!(schema, "# ==========================================")
109 .expect("writing to String is infallible");
110 writeln!(schema, "# Mutation Operations").expect("writing to String is infallible");
111 writeln!(schema, "# ==========================================")
112 .expect("writing to String is infallible");
113 writeln!(schema).expect("writing to String is infallible");
114 self.generate_mutation_type(&mut schema, table);
115 writeln!(schema).expect("writing to String is infallible");
116 }
117
118 writeln!(schema, "# ==========================================")
120 .expect("writing to String is infallible");
121 writeln!(schema, "# Schema Definition").expect("writing to String is infallible");
122 writeln!(schema, "# ==========================================")
123 .expect("writing to String is infallible");
124 writeln!(schema).expect("writing to String is infallible");
125 writeln!(schema, "schema {{").expect("writing to String is infallible");
126 if self.generate_queries {
127 writeln!(schema, " query: Query").expect("writing to String is infallible");
128 }
129 if self.generate_mutations {
130 writeln!(schema, " mutation: Mutation").expect("writing to String is infallible");
131 }
132 writeln!(schema, "}}").expect("writing to String is infallible");
133
134 schema
135 }
136
137 fn generate_domain_type(&self, schema: &mut String, domain: &DomainInfo) {
139 let type_name = Self::to_graphql_type_name(&domain.name);
140
141 if self.include_descriptions {
142 if let Some(ref desc) = domain.description {
143 writeln!(schema, "\"\"\"\n{}\n\"\"\"", desc)
144 .expect("writing to String is infallible");
145 } else {
146 writeln!(schema, "\"\"\"\nDomain: {}\n\"\"\"", domain.name)
147 .expect("writing to String is infallible");
148 }
149 }
150
151 writeln!(schema, "type {} {{", type_name).expect("writing to String is infallible");
152 writeln!(schema, " \"Unique identifier\"").expect("writing to String is infallible");
153 writeln!(schema, " id: ID!").expect("writing to String is infallible");
154 writeln!(
155 schema,
156 " \"Integer index (0 to {})\"",
157 domain.cardinality - 1
158 )
159 .expect("writing to String is infallible");
160 writeln!(schema, " index: Int!").expect("writing to String is infallible");
161 writeln!(schema, "}}").expect("writing to String is infallible");
162 }
163
164 fn generate_predicate_type(
166 &self,
167 schema: &mut String,
168 predicate: &PredicateInfo,
169 _table: &SymbolTable,
170 ) {
171 let type_name = Self::to_graphql_type_name(&predicate.name);
172
173 if self.include_descriptions {
174 if let Some(ref desc) = predicate.description {
175 writeln!(schema, "\"\"\"\n{}\n\"\"\"", desc)
176 .expect("writing to String is infallible");
177 } else {
178 writeln!(schema, "\"\"\"\nPredicate: {}\n\"\"\"", predicate.name)
179 .expect("writing to String is infallible");
180 }
181 }
182
183 writeln!(schema, "type {} {{", type_name).expect("writing to String is infallible");
184
185 writeln!(schema, " \"Unique identifier\"").expect("writing to String is infallible");
187 writeln!(schema, " id: ID!").expect("writing to String is infallible");
188
189 for (i, domain_name) in predicate.arg_domains.iter().enumerate() {
191 let field_name = format!("arg{}", i);
192 let field_type = Self::to_graphql_type_name(domain_name);
193 writeln!(schema, " \"Argument {} of type {}\"", i, domain_name)
194 .expect("writing to String is infallible");
195 writeln!(schema, " {}: {}!", field_name, field_type)
196 .expect("writing to String is infallible");
197 }
198
199 writeln!(schema, "}}").expect("writing to String is infallible");
200 }
201
202 fn generate_query_type(&self, schema: &mut String, table: &SymbolTable) {
204 writeln!(schema, "\"\"\"").expect("writing to String is infallible");
205 writeln!(schema, "Root query type for retrieving data")
206 .expect("writing to String is infallible");
207 writeln!(schema, "\"\"\"").expect("writing to String is infallible");
208 writeln!(schema, "type Query {{").expect("writing to String is infallible");
209
210 for domain in table.domains.values() {
212 let type_name = Self::to_graphql_type_name(&domain.name);
213 let field_name = Self::to_graphql_field_name(&domain.name);
214
215 writeln!(schema, " \"Get {} by ID\"", domain.name)
216 .expect("writing to String is infallible");
217 writeln!(schema, " {}(id: ID!): {}", field_name, type_name)
218 .expect("writing to String is infallible");
219 writeln!(schema).expect("writing to String is infallible");
220
221 writeln!(schema, " \"List all {}s\"", domain.name)
222 .expect("writing to String is infallible");
223 writeln!(schema, " {}s: [{}!]!", field_name, type_name)
224 .expect("writing to String is infallible");
225 writeln!(schema).expect("writing to String is infallible");
226 }
227
228 for predicate in table.predicates.values() {
230 let type_name = Self::to_graphql_type_name(&predicate.name);
231 let field_name = Self::to_graphql_field_name(&predicate.name);
232
233 writeln!(schema, " \"Query {} predicate\"", predicate.name)
234 .expect("writing to String is infallible");
235
236 write!(schema, " {}(", field_name).expect("writing to String is infallible");
238 for (i, domain_name) in predicate.arg_domains.iter().enumerate() {
239 if i > 0 {
240 write!(schema, ", ").expect("writing to String is infallible");
241 }
242 let arg_type = Self::to_graphql_type_name(domain_name);
243 write!(schema, "arg{}: {}", i, arg_type).expect("writing to String is infallible");
244 }
245 writeln!(schema, "): [{}!]!", type_name).expect("writing to String is infallible");
246 writeln!(schema).expect("writing to String is infallible");
247 }
248
249 writeln!(schema, "}}").expect("writing to String is infallible");
250 }
251
252 fn generate_mutation_type(&self, schema: &mut String, table: &SymbolTable) {
254 writeln!(schema, "\"\"\"").expect("writing to String is infallible");
255 writeln!(schema, "Root mutation type for modifying data")
256 .expect("writing to String is infallible");
257 writeln!(schema, "\"\"\"").expect("writing to String is infallible");
258 writeln!(schema, "type Mutation {{").expect("writing to String is infallible");
259
260 for predicate in table.predicates.values() {
262 let type_name = Self::to_graphql_type_name(&predicate.name);
263
264 writeln!(schema, " \"Add {} instance\"", predicate.name)
266 .expect("writing to String is infallible");
267 write!(schema, " add{}(", type_name).expect("writing to String is infallible");
268 for (i, domain_name) in predicate.arg_domains.iter().enumerate() {
269 if i > 0 {
270 write!(schema, ", ").expect("writing to String is infallible");
271 }
272 let arg_type = Self::to_graphql_type_name(domain_name);
273 write!(schema, "arg{}: {}!", i, arg_type).expect("writing to String is infallible");
274 }
275 writeln!(schema, "): {}!", type_name).expect("writing to String is infallible");
276 writeln!(schema).expect("writing to String is infallible");
277
278 writeln!(schema, " \"Remove {} instance\"", predicate.name)
280 .expect("writing to String is infallible");
281 writeln!(schema, " remove{}(id: ID!): Boolean!", type_name)
282 .expect("writing to String is infallible");
283 writeln!(schema).expect("writing to String is infallible");
284 }
285
286 writeln!(schema, "}}").expect("writing to String is infallible");
287 }
288
289 fn to_graphql_type_name(name: &str) -> String {
291 RustCodegen::to_type_name(name) }
293
294 pub(super) fn to_graphql_field_name(name: &str) -> String {
296 let parts: Vec<&str> = name.split('_').collect();
297 if parts.is_empty() {
298 return String::new();
299 }
300
301 let mut result = parts[0].to_lowercase();
302 for part in &parts[1..] {
303 if let Some(first_char) = part.chars().next() {
304 result.push_str(&first_char.to_uppercase().to_string());
305 result.push_str(&part[first_char.len_utf8()..]);
306 }
307 }
308 result
309 }
310}