Skip to main content

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}