1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use serde::{Deserialize, Serialize};
use crate::schema::{
field_type::{DeprecationInfo, FieldType},
graphql_value::GraphQLValue,
};
/// Query/mutation/subscription argument definition.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArgumentDefinition {
/// Argument name.
pub name: String,
/// Argument type.
pub arg_type: FieldType,
/// Is this argument optional?
#[serde(default)]
pub nullable: bool,
/// Default value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_value: Option<GraphQLValue>,
/// Description.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// Deprecation information (from @deprecated directive).
/// When set, this argument is marked as deprecated in the schema.
/// Per GraphQL spec, deprecated arguments should still be accepted but
/// clients are encouraged to migrate to alternatives.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deprecation: Option<DeprecationInfo>,
}
impl ArgumentDefinition {
/// Create a new required argument.
#[must_use]
pub fn new(name: impl Into<String>, arg_type: FieldType) -> Self {
Self {
name: name.into(),
arg_type,
nullable: false,
default_value: None,
description: None,
deprecation: None,
}
}
/// Create a new optional argument.
#[must_use]
pub fn optional(name: impl Into<String>, arg_type: FieldType) -> Self {
Self {
name: name.into(),
arg_type,
nullable: true,
default_value: None,
description: None,
deprecation: None,
}
}
/// Attach a description to this argument.
#[must_use]
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
/// Mark this argument as deprecated.
///
/// # Example
///
/// ```
/// use fraiseql_core::schema::{ArgumentDefinition, FieldType};
///
/// let arg = ArgumentDefinition::optional("oldLimit", FieldType::Int)
/// .deprecated(Some("Use 'first' instead".to_string()));
/// assert!(arg.is_deprecated());
/// ```
#[must_use]
pub fn deprecated(mut self, reason: Option<String>) -> Self {
self.deprecation = Some(DeprecationInfo { reason });
self
}
/// Check if this argument is deprecated.
#[must_use]
pub const fn is_deprecated(&self) -> bool {
self.deprecation.is_some()
}
/// Get the deprecation reason if deprecated.
#[must_use]
pub fn deprecation_reason(&self) -> Option<&str> {
self.deprecation.as_ref().and_then(|d| d.reason.as_deref())
}
}
/// Auto-wired query parameters.
///
/// These are standard parameters automatically added to list queries.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)] // Reason: these are intentional feature flags
pub struct AutoParams {
/// Enable `where` filtering.
#[serde(default)]
pub has_where: bool,
/// Enable `orderBy` sorting.
#[serde(default)]
pub has_order_by: bool,
/// Enable `limit` pagination.
#[serde(default)]
pub has_limit: bool,
/// Enable `offset` pagination.
#[serde(default)]
pub has_offset: bool,
}
impl AutoParams {
/// Create with all auto-params enabled (common for list queries).
#[must_use]
pub const fn all() -> Self {
Self {
has_where: true,
has_order_by: true,
has_limit: true,
has_offset: true,
}
}
/// Create with no auto-params (common for single-item queries).
#[must_use]
pub fn none() -> Self {
Self::default()
}
/// The shape the Relay spec mandates: filter and sort, but no `limit`/`offset`.
///
/// A connection is paged by `first`/`after` — offering `limit` and `offset` beside
/// them would be a second, unsynchronised way to move the same cursor. The compiler
/// applies this unconditionally to a relay query, ahead of the list defaults
/// (`QueryConverter::convert_query`).
///
/// Named rather than written out at each site because the shape is authored twice —
/// once by the compiler, once by `TestQueryBuilder` — and a fixture that disagrees
/// with the compiler about a flag is a test asserting over a parameter the engine
/// would have dropped (#1290).
#[must_use]
pub const fn relay() -> Self {
Self {
has_where: true,
has_order_by: true,
has_limit: false,
has_offset: false,
}
}
}