1use std::{borrow::Cow, fmt::Write};
2
3use super::indent::indented;
4
5#[derive(Debug, Default)]
6pub struct SelectionSet {
8 pub(super) selections: Vec<Selection>,
9}
10
11#[derive(Debug)]
12pub enum Selection {
14 Field(FieldSelection),
16 InlineFragment(InlineFragment),
18}
19
20#[derive(Debug)]
21pub struct FieldSelection {
23 pub(super) name: &'static str,
24 pub(super) alias: Option<Cow<'static, str>>,
25 pub(super) arguments: Vec<Argument>,
26 pub(super) directives: Vec<Directive>,
27 pub(super) children: SelectionSet,
28}
29
30#[derive(Debug, PartialEq)]
31pub struct Argument {
33 pub(super) name: Cow<'static, str>,
34 pub(super) value: InputLiteral,
35}
36
37impl Argument {
38 pub fn new(name: &'static str, value: InputLiteral) -> Self {
40 Argument {
41 name: Cow::Borrowed(name),
42 value,
43 }
44 }
45
46 pub fn from_cow_name(name: Cow<'static, str>, value: InputLiteral) -> Self {
48 Argument { name, value }
49 }
50}
51
52#[derive(Debug, PartialEq)]
53pub enum InputLiteral {
57 Int(i32),
59 Float(f64),
61 Bool(bool),
63 String(Cow<'static, str>),
65 Id(String),
67 Object(Vec<Argument>),
69 List(Vec<InputLiteral>),
71 Variable(&'static str),
73 Null,
75 EnumValue(&'static str),
77}
78
79#[derive(Debug, PartialEq)]
80pub struct Directive {
82 pub(super) name: Cow<'static, str>,
83 pub(super) arguments: Vec<Argument>,
84}
85
86#[derive(Debug, Default)]
87pub struct InlineFragment {
89 pub(super) on_clause: Option<&'static str>,
90 pub(super) children: SelectionSet,
91}
92
93impl FieldSelection {
94 pub fn new(name: &'static str) -> FieldSelection {
96 FieldSelection {
97 name,
98 alias: None,
99 arguments: Vec::new(),
100 directives: Vec::new(),
101 children: SelectionSet::default(),
102 }
103 }
104}
105
106impl std::fmt::Display for SelectionSet {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 if !self.selections.is_empty() {
109 writeln!(f, " {{")?;
110 for child in &self.selections {
111 write!(indented(f, 2), "{}", child)?;
112 }
113 write!(f, "}}")?;
114 }
115 writeln!(f)
116 }
117}
118
119impl std::fmt::Display for Selection {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 match self {
122 Selection::Field(field_selection) => {
123 if let Some(alias) = &field_selection.alias {
124 write!(f, "{}: ", alias)?;
125 }
126
127 write!(f, "{}", field_selection.name)?;
128
129 if !field_selection.arguments.is_empty() {
130 write!(f, "(")?;
131 let mut first = true;
132 for arg in &field_selection.arguments {
133 if !first {
134 write!(f, ", ")?;
135 }
136 first = false;
137 write!(f, "{}", arg)?;
138 }
139 write!(f, ")")?;
140 }
141
142 for Directive { name, arguments } in &field_selection.directives {
143 write!(f, " @{name}")?;
144 if !arguments.is_empty() {
145 write!(f, "(")?;
146 let mut first = true;
147 for arg in arguments {
148 if !first {
149 write!(f, ", ")?;
150 }
151 first = false;
152 write!(f, "{}", arg)?;
153 }
154 write!(f, ")")?;
155 }
156 }
157 write!(f, "{}", field_selection.children)
158 }
159 Selection::InlineFragment(inline_fragment) => {
160 if !inline_fragment.children.selections.is_empty() {
162 write!(f, "...")?;
163 if let Some(on_type) = inline_fragment.on_clause {
164 write!(f, " on {}", on_type)?;
165 }
166 write!(f, "{}", inline_fragment.children)?;
167 }
168 Ok(())
169 }
170 }
171 }
172}
173
174impl std::fmt::Display for Argument {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 write!(f, "{}: {}", self.name, self.value)
177 }
178}
179
180impl std::fmt::Display for InputLiteral {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 match self {
183 InputLiteral::Int(val) => write!(f, "{}", val),
184 InputLiteral::Float(val) => write!(f, "{}", val),
185 InputLiteral::Bool(val) => write!(f, "{}", val),
186 InputLiteral::String(val) => {
187 let val = escape_string(val);
188 write!(f, "\"{val}\"")
189 }
190 InputLiteral::Id(val) => write!(f, "\"{}\"", val),
191 InputLiteral::Object(fields) => {
192 write!(f, "{{")?;
193 for (i, field) in fields.iter().enumerate() {
194 if i != 0 {
195 write!(f, ", ")?;
196 }
197 write!(f, "{}: {}", field.name, field.value)?;
198 }
199 write!(f, "}}")
200 }
201 InputLiteral::List(vals) => {
202 write!(f, "[")?;
203 for (i, val) in vals.iter().enumerate() {
204 if i != 0 {
205 write!(f, ", ")?;
206 }
207 write!(f, "{}", val)?;
208 }
209 write!(f, "]")
210 }
211 InputLiteral::Variable(name) => {
212 write!(f, "${}", name)
213 }
214 InputLiteral::Null => {
215 write!(f, "null")
216 }
217 InputLiteral::EnumValue(name) => {
218 write!(f, "{name}")
219 }
220 }
221 }
222}
223
224fn escape_string(src: &str) -> String {
225 let mut dest = String::with_capacity(src.len());
226
227 for character in src.chars() {
228 match character {
229 '"' | '\\' | '\n' | '\r' | '\t' => {
230 dest.extend(character.escape_default());
231 }
232 other if other.is_control() => {
233 dest.extend(character.escape_default());
234 }
235 _ => dest.push(character),
236 }
237 }
238
239 dest
240}