fraiseql_cli/schema/intermediate/fragments.rs
1//! Fragment/directive structs: `IntermediateFragment`, `IntermediateFragmentField`,
2//! `IntermediateFragmentFieldDef`, `IntermediateDirective`, `IntermediateAppliedDirective`.
3
4use serde::{Deserialize, Serialize};
5
6use super::operations::IntermediateArgument;
7
8// =============================================================================
9// Fragment and Directive Definitions (GraphQL Spec §2.9-2.12)
10// =============================================================================
11
12/// Fragment definition in intermediate format.
13///
14/// Fragments are reusable field selections that can be spread into queries.
15/// Per GraphQL spec §2.9-2.10, fragments have a type condition and field list.
16///
17/// # Example JSON
18///
19/// ```json
20/// {
21/// "name": "UserFields",
22/// "on": "User",
23/// "fields": ["id", "name", "email"]
24/// }
25/// ```
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct IntermediateFragment {
28 /// Fragment name (e.g., "UserFields")
29 pub name: String,
30
31 /// Type condition - the type this fragment applies to (e.g., "User")
32 #[serde(rename = "on")]
33 pub type_condition: String,
34
35 /// Fields to select (can be field names or nested fragment spreads)
36 pub fields: Vec<IntermediateFragmentField>,
37
38 /// Fragment description (from docstring)
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub description: Option<String>,
41}
42
43/// Fragment field selection - either a simple field or a nested object/fragment spread.
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(untagged)]
46#[non_exhaustive]
47pub enum IntermediateFragmentField {
48 /// Simple field name (e.g., "id", "name")
49 Simple(String),
50
51 /// Complex field with nested selections or directives
52 Complex(IntermediateFragmentFieldDef),
53}
54
55/// Complex fragment field definition with optional alias, directives, and nested fields.
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57pub struct IntermediateFragmentFieldDef {
58 /// Field name (source field in the type)
59 pub name: String,
60
61 /// Output alias (optional, per GraphQL spec §2.13)
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub alias: Option<String>,
64
65 /// Nested field selections (for object fields)
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub fields: Option<Vec<IntermediateFragmentField>>,
68
69 /// Fragment spread (e.g., "...UserFields")
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub spread: Option<String>,
72
73 /// Applied directives (e.g., @skip, @include)
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub directives: Option<Vec<IntermediateAppliedDirective>>,
76}
77
78/// Directive definition in intermediate format.
79///
80/// Directives provide a way to describe alternate runtime execution and type validation.
81/// Per GraphQL spec §2.12, directives can be applied to various locations.
82///
83/// # Example JSON
84///
85/// ```json
86/// {
87/// "name": "auth",
88/// "locations": ["FIELD_DEFINITION", "OBJECT"],
89/// "arguments": [{"name": "role", "type": "String", "nullable": false}],
90/// "description": "Requires authentication with specified role"
91/// }
92/// ```
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94pub struct IntermediateDirective {
95 /// Directive name (without @, e.g., "auth", "deprecated")
96 pub name: String,
97
98 /// Valid locations where this directive can be applied
99 pub locations: Vec<String>,
100
101 /// Directive arguments
102 #[serde(default)]
103 pub arguments: Vec<IntermediateArgument>,
104
105 /// Whether the directive can be applied multiple times
106 #[serde(default)]
107 pub repeatable: bool,
108
109 /// Directive description
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub description: Option<String>,
112}
113
114/// An applied directive instance (used on fields, types, etc.).
115///
116/// # Example JSON
117///
118/// ```json
119/// {
120/// "name": "skip",
121/// "arguments": {"if": true}
122/// }
123/// ```
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125pub struct IntermediateAppliedDirective {
126 /// Directive name (without @)
127 pub name: String,
128
129 /// Directive arguments as key-value pairs
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub arguments: Option<serde_json::Value>,
132}