spikard_cli/codegen/graphql/mod.rs
1//! GraphQL Schema Definition Language (SDL) and introspection parsing
2//!
3//! This module provides parsing and type extraction for GraphQL schemas in both
4//! SDL (Schema Definition Language) and JSON introspection formats.
5
6pub mod generators;
7pub mod sdl;
8pub mod spec_parser;
9
10pub use spec_parser::{
11 GraphQLArgument, GraphQLDirective, GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLSchema, GraphQLType,
12 TypeKind, parse_graphql_schema, parse_graphql_sdl, parse_graphql_sdl_string,
13};
14
15pub use generators::{GraphQLGenerator, RustGenerator};
16
17use anyhow::Result;
18
19/// Generate Python GraphQL code from a schema
20///
21/// Parses the GraphQL schema string and generates complete Python code with type
22/// definitions, resolver implementations, and schema configuration based on the
23/// target specification. Generated code follows Python 3.10+ conventions using
24/// msgspec for type-safe data serialization and Ariadne for GraphQL schema binding.
25///
26/// # Generated Code Features
27///
28/// * **Type Definitions**: Python dataclasses using `msgspec.Struct` with
29/// `frozen=True` and `kw_only=True` for immutability and explicitness
30/// * **Enums**: Python enums inheriting from `str` for GraphQL enum types
31/// * **Input Objects**: msgspec Structs for GraphQL input types
32/// * **Union Types**: Python type aliases (e.g., `User | Post | Any`)
33/// * **Type Hints**: Full type annotations using Python 3.10+ syntax (e.g., `str | None`
34/// instead of `Optional[str]`, `list[Item]` instead of `List[Item]`)
35/// * **Resolvers**: Async resolver functions with proper type hints and parameter handling
36/// * **Schema Definition**: Ariadne-compatible schema with embedded SDL and resolver setup
37/// * **Docstrings**: NumPy-style docstrings extracted from GraphQL descriptions
38///
39/// # Arguments
40///
41/// * `schema` - GraphQL schema as a string (SDL format)
42/// * `target` - Generation target specifying what to generate:
43/// * `"all"` - Complete code: types, resolvers, and schema definition
44/// * `"types"` - Type definitions and enums only
45/// * `"resolvers"` - Async resolver function stubs with type hints
46/// * `"schema"` - Ariadne schema definition with embedded SDL
47/// * Any other value defaults to "all"
48///
49/// # Returns
50///
51/// Generated Python code as a `String`, or an `anyhow::Error` if parsing or
52/// generation fails (e.g., invalid GraphQL SDL syntax).
53///
54/// # Type Mapping
55///
56/// GraphQL types are mapped to Python as follows:
57/// * `String` → `str`
58/// * `Int` → `int`
59/// * `Float` → `float`
60/// * `Boolean` → `bool`
61/// * `ID` → `str`
62/// * Custom scalars → `str` (unless defined in schema)
63/// * Nullable types → `T | None` (e.g., `str | None`)
64/// * List types → `list[T]` (e.g., `list[str]` or `list[str | None]`)
65/// * Union types → `Type1 | Type2 | ... | Any`
66///
67/// # Examples
68///
69/// Generate all Python code for a simple query:
70/// ```ignore
71/// let schema = r#"
72/// type Query {
73/// hello: String!
74/// user(id: ID!): User
75/// }
76///
77/// type User {
78/// id: ID!
79/// name: String!
80/// email: String
81/// }
82/// "#;
83///
84/// let code = generate_python_graphql(schema, "all")?;
85/// println!("{}", code);
86/// ```
87///
88/// Generate only type definitions:
89/// ```ignore
90/// let code = generate_python_graphql(schema, "types")?;
91/// // Contains: msgspec Structs, Enums, type unions
92/// ```
93///
94/// Generate resolver function stubs:
95/// ```ignore
96/// let code = generate_python_graphql(schema, "resolvers")?;
97/// // Contains: async def resolve_* functions with type hints
98/// ```
99pub fn generate_python_graphql(schema: &str, target: &str) -> Result<String> {
100 use generators::GraphQLGenerator;
101 use generators::python::PythonGenerator;
102
103 let parsed_schema = parse_graphql_sdl_string(schema)?;
104
105 let generator = PythonGenerator;
106
107 match target {
108 "all" => generator.generate_complete(&parsed_schema),
109 "types" => generator.generate_types(&parsed_schema),
110 "resolvers" => generator.generate_resolvers(&parsed_schema),
111 "schema" => generator.generate_schema_definition(&parsed_schema),
112 _ => generator.generate_complete(&parsed_schema),
113 }
114}
115
116/// Generate TypeScript GraphQL code from a schema
117///
118/// Parses the GraphQL schema string and generates complete TypeScript code
119/// based on the target specification.
120///
121/// # Arguments
122///
123/// * `schema` - GraphQL schema as a string (SDL format)
124/// * `target` - Generation target: "all" (complete), "types" (types only),
125/// "resolvers" (resolvers), or "schema" (schema definition)
126///
127/// # Returns
128///
129/// Generated TypeScript code as a string, or an error if parsing/generation fails
130pub fn generate_typescript_graphql(schema: &str, target: &str) -> Result<String> {
131 use generators::GraphQLGenerator;
132 use generators::typescript::TypeScriptGenerator;
133
134 let parsed_schema = parse_graphql_sdl_string(schema)?;
135
136 let generator = TypeScriptGenerator;
137
138 match target {
139 "all" => generator.generate_complete(&parsed_schema),
140 "types" => generator.generate_types(&parsed_schema),
141 "resolvers" => generator.generate_resolvers(&parsed_schema),
142 "schema" => generator.generate_schema_definition(&parsed_schema),
143 _ => generator.generate_complete(&parsed_schema),
144 }
145}
146
147/// Generate Rust GraphQL code from a schema
148///
149/// Parses the GraphQL schema string and generates complete Rust code with async-graphql
150/// types, resolvers, and schema definition based on the target specification.
151///
152/// # Arguments
153///
154/// * `schema` - GraphQL schema as a string (SDL format)
155/// * `target` - Generation target: "all" (complete), "types" (types only),
156/// "resolvers" (Query/Mutation/Subscription), or "schema" (schema builder)
157///
158/// # Returns
159///
160/// Generated Rust code as a string, or an error if parsing/generation fails
161pub fn generate_rust_graphql(schema: &str, target: &str) -> Result<String> {
162 let parsed_schema = parse_graphql_sdl_string(schema)?;
163
164 let generator = RustGenerator::new();
165
166 match target {
167 "all" => generator.generate_complete(&parsed_schema),
168 "types" => generator.generate_types(&parsed_schema),
169 "resolvers" => generator.generate_resolvers(&parsed_schema),
170 "schema" => generator.generate_schema_definition(&parsed_schema),
171 _ => generator.generate_complete(&parsed_schema),
172 }
173}
174
175/// Generate Ruby GraphQL code from a schema
176///
177/// Parses the GraphQL schema string and generates idiomatic Ruby code with graphql-ruby
178/// class definitions for types, resolvers, and schema configuration based on the
179/// target specification. Supports RBS (Ruby Signature) type definition generation for
180/// integration with Steep static type checker.
181///
182/// # Arguments
183///
184/// * `schema` - GraphQL schema as a string (SDL format)
185/// * `target` - Generation target: "all" (complete), "types" (types only),
186/// "resolvers" (resolver classes), "schema" (schema definition),
187/// or "rbs" (RBS type signatures for Steep)
188///
189/// # Returns
190///
191/// Generated Ruby code or RBS signatures as a string, or an error if parsing/generation fails
192///
193/// # Examples
194///
195/// Generate all Ruby code:
196/// ```ignore
197/// let schema = r#"
198/// type Query {
199/// hello: String!
200/// }
201/// "#;
202/// let code = generate_ruby_graphql(schema, "all")?;
203/// println!("{}", code);
204/// ```
205///
206/// Generate RBS type signatures:
207/// ```ignore
208/// let rbs = generate_ruby_graphql(schema, "rbs")?;
209/// // Output is RBS syntax compatible with Steep type checker
210/// ```
211pub fn generate_ruby_graphql(schema: &str, target: &str) -> Result<String> {
212 use generators::GraphQLGenerator;
213 use generators::ruby::RubyGenerator;
214
215 let parsed_schema = parse_graphql_sdl_string(schema)?;
216 let generator = RubyGenerator;
217
218 match target {
219 "types" => generator.generate_types(&parsed_schema),
220 "resolvers" => generator.generate_resolvers(&parsed_schema),
221 "schema" => generator.generate_schema_definition(&parsed_schema),
222 "rbs" => generator.generate_type_signatures(&parsed_schema),
223 "all" => generator.generate_complete(&parsed_schema),
224 _ => generator.generate_complete(&parsed_schema),
225 }
226}
227
228/// Generate PHP GraphQL code from a schema
229///
230/// Parses the GraphQL schema string and generates complete PHP code with type definitions,
231/// resolver implementations, and schema configuration based on the target specification.
232/// Generated code uses PSR-4 namespacing, `strict_types` declarations, typed properties,
233/// and webonyx/graphql-php library for schema binding.
234///
235/// # Arguments
236///
237/// * `schema` - GraphQL schema as a string (SDL format)
238/// * `target` - Generation target: "all" (complete), "types" (types only),
239/// "resolvers" (resolver classes), or "schema" (schema definition)
240///
241/// # Returns
242///
243/// Generated PHP code as a string with:
244/// - `<?php` opening tag and `declare(strict_types=1);`
245/// - PSR-4 namespace declaration under GraphQL namespace
246/// - Class definitions for object types, input types, and enums (PHP 8.1+)
247/// - Typed properties with appropriate nullability markers
248/// - Resolver method signatures with return type declarations
249/// - Or an error if parsing/generation fails
250///
251/// # Examples
252///
253/// ```ignore
254/// let schema = r#"
255/// type Query {
256/// user(id: ID!): User
257/// }
258/// type User {
259/// id: ID!
260/// name: String!
261/// }
262/// "#;
263/// let code = generate_php_graphql(schema, "all")?;
264/// println!("{}", code);
265/// ```
266pub fn generate_php_graphql(schema: &str, target: &str) -> Result<String> {
267 use generators::GraphQLGenerator;
268 use generators::php::PhpGenerator;
269
270 let parsed_schema = parse_graphql_sdl_string(schema)?;
271
272 let generator = PhpGenerator;
273
274 match target {
275 "all" => generator.generate_complete(&parsed_schema),
276 "types" => generator.generate_types(&parsed_schema),
277 "resolvers" => generator.generate_resolvers(&parsed_schema),
278 "schema" => generator.generate_schema_definition(&parsed_schema),
279 _ => generator.generate_complete(&parsed_schema),
280 }
281}
282
283/// Generate Elixir GraphQL code from a schema.
284///
285/// Parses GraphQL SDL and emits `Spikard.Router`-based scaffolding with typed
286/// schema modules, resolver stubs, and an embedded SDL definition.
287pub fn generate_elixir_graphql(schema: &str, target: &str) -> Result<String> {
288 use generators::GraphQLGenerator;
289 use generators::elixir::ElixirGenerator;
290
291 let parsed_schema = parse_graphql_sdl_string(schema)?;
292 let generator = ElixirGenerator;
293
294 match target {
295 "all" => generator.generate_complete(&parsed_schema),
296 "types" => generator.generate_types(&parsed_schema),
297 "resolvers" => generator.generate_resolvers(&parsed_schema),
298 "schema" => generator.generate_schema_definition(&parsed_schema),
299 _ => generator.generate_complete(&parsed_schema),
300 }
301}