1use std::{collections::BTreeMap, fmt};
2
3use combine::easy::{Error, Info};
4use combine::{choice, many, many1, optional, position, StdParseResult};
5use combine::{parser, Parser};
6
7use crate::helpers::{ident, kind, name, punct};
8use crate::position::Pos;
9use crate::tokenizer::{Kind as T, Token, TokenStream};
10
11pub trait Text<'a>: 'a {
14 type Value: 'a
15 + From<&'a str>
16 + AsRef<str>
17 + std::borrow::Borrow<str>
18 + PartialEq
19 + Eq
20 + PartialOrd
21 + Ord
22 + fmt::Debug
23 + Clone;
24}
25
26impl<'a> Text<'a> for &'a str {
27 type Value = Self;
28}
29
30impl<'a> Text<'a> for String {
31 type Value = String;
32}
33
34impl<'a> Text<'a> for std::borrow::Cow<'a, str> {
35 type Value = Self;
36}
37
38#[derive(Debug, Clone, PartialEq)]
39pub struct Directive<'a, T: Text<'a>> {
40 pub position: Pos,
41 pub name: T::Value,
42 pub arguments: Vec<(T::Value, Value<'a, T>)>,
43}
44
45#[derive(Debug, Clone, PartialEq)]
52pub struct Number(pub(crate) i64);
55
56#[derive(Debug, Clone, PartialEq)]
57pub enum Value<'a, T: Text<'a>> {
58 Variable(T::Value),
59 Int(Number),
60 Float(f64),
61 String(String),
62 Boolean(bool),
63 Null,
64 Enum(T::Value),
65 List(Vec<Value<'a, T>>),
66 Object(BTreeMap<T::Value, Value<'a, T>>),
67}
68
69impl<'a, T: Text<'a>> Value<'a, T> {
70 pub fn into_static(&self) -> Value<'static, String> {
71 match self {
72 Self::Variable(v) => Value::Variable(v.as_ref().into()),
73 Self::Int(i) => Value::Int(i.clone()),
74 Self::Float(f) => Value::Float(*f),
75 Self::String(s) => Value::String(s.clone()),
76 Self::Boolean(b) => Value::Boolean(*b),
77 Self::Null => Value::Null,
78 Self::Enum(v) => Value::Enum(v.as_ref().into()),
79 Self::List(l) => Value::List(l.iter().map(|e| e.into_static()).collect()),
80 Self::Object(o) => Value::Object(
81 o.iter()
82 .map(|(k, v)| (k.as_ref().into(), v.into_static()))
83 .collect(),
84 ),
85 }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq)]
90pub enum Type<'a, T: Text<'a>> {
91 NamedType(T::Value),
92 ListType(Box<Type<'a, T>>),
93 NonNullType(Box<Type<'a, T>>),
94}
95
96impl Number {
97 pub fn as_i64(&self) -> Option<i64> {
99 Some(self.0)
100 }
101}
102
103impl From<i32> for Number {
104 fn from(i: i32) -> Self {
105 Number(i as i64)
106 }
107}
108
109pub fn directives<'a, T>(
110 input: &mut TokenStream<'a>,
111) -> StdParseResult<Vec<Directive<'a, T>>, TokenStream<'a>>
112where
113 T: Text<'a>,
114{
115 many(
116 position()
117 .skip(punct("@"))
118 .and(name::<'a, T>())
119 .and(parser(arguments))
120 .map(|((position, name), arguments)| Directive {
121 position,
122 name,
123 arguments,
124 }),
125 )
126 .parse_stream(input)
127 .into_result()
128}
129
130#[allow(clippy::type_complexity)]
131pub fn arguments<'a, T>(
132 input: &mut TokenStream<'a>,
133) -> StdParseResult<Vec<(T::Value, Value<'a, T>)>, TokenStream<'a>>
134where
135 T: Text<'a>,
136{
137 optional(
138 punct("(")
139 .with(many1(name::<'a, T>().skip(punct(":")).and(parser(value))))
140 .skip(punct(")")),
141 )
142 .map(|opt| opt.unwrap_or_default())
143 .parse_stream(input)
144 .into_result()
145}
146
147pub fn int_value<'a, S>(
148 input: &mut TokenStream<'a>,
149) -> StdParseResult<Value<'a, S>, TokenStream<'a>>
150where
151 S: Text<'a>,
152{
153 kind(T::IntValue)
154 .and_then(|tok| tok.value.parse())
155 .map(Number)
156 .map(Value::Int)
157 .parse_stream(input)
158 .into_result()
159}
160
161pub fn float_value<'a, S>(
162 input: &mut TokenStream<'a>,
163) -> StdParseResult<Value<'a, S>, TokenStream<'a>>
164where
165 S: Text<'a>,
166{
167 kind(T::FloatValue)
168 .and_then(|tok| tok.value.parse())
169 .map(Value::Float)
170 .parse_stream(input)
171 .into_result()
172}
173
174fn unquote_block_string(src: &str) -> Result<String, Error<Token<'_>, Token<'_>>> {
175 debug_assert!(src.starts_with("\"\"\"") && src.ends_with("\"\"\""));
176 let lines = src[3..src.len() - 3].lines();
177
178 let mut common_indent = usize::MAX;
179 let mut first_non_empty_line: Option<usize> = None;
180 let mut last_non_empty_line = 0;
181 for (idx, line) in lines.clone().enumerate() {
182 let indent = line.len() - line.trim_start().len();
183 if indent == line.len() {
184 continue;
185 }
186
187 first_non_empty_line.get_or_insert(idx);
188 last_non_empty_line = idx;
189
190 if idx != 0 {
191 common_indent = std::cmp::min(common_indent, indent);
192 }
193 }
194
195 if first_non_empty_line.is_none() {
196 return Ok("".to_string());
198 }
199 let first_non_empty_line = first_non_empty_line.unwrap();
200
201 let mut result = String::with_capacity(src.len() - 6);
202 let mut lines = lines
203 .enumerate()
204 .skip(first_non_empty_line)
206 .take(last_non_empty_line - first_non_empty_line + 1)
207 .map(|(idx, line)| {
209 if idx != 0 && line.len() >= common_indent {
210 &line[common_indent..]
211 } else {
212 line
213 }
214 })
215 .map(|x| x.replace(r#"\""""#, r#"""""#));
217
218 if let Some(line) = lines.next() {
219 result.push_str(&line);
220
221 for line in lines {
222 result.push_str("\n");
223 result.push_str(&line);
224 }
225 }
226 return Ok(result);
227}
228
229fn unquote_string(s: &str) -> Result<String, Error<Token, Token>> {
230 let mut res = String::with_capacity(s.len());
231 debug_assert!(s.starts_with('"') && s.ends_with('"'));
232 let mut chars = s[1..s.len() - 1].chars();
233 let mut temp_code_point = String::with_capacity(4);
234 while let Some(c) = chars.next() {
235 match c {
236 '\\' => {
237 match chars.next().expect("slash cant be at the end") {
238 c @ '"' | c @ '\\' | c @ '/' => res.push(c),
239 'b' => res.push('\u{0010}'),
240 'f' => res.push('\u{000C}'),
241 'n' => res.push('\n'),
242 'r' => res.push('\r'),
243 't' => res.push('\t'),
244 'u' => {
245 temp_code_point.clear();
246 for _ in 0..4 {
247 match chars.next() {
248 Some(inner_c) => temp_code_point.push(inner_c),
249 None => {
250 return Err(Error::Unexpected(Info::Owned(
251 format_args!(
252 "\\u must have 4 characters after it, only found '{}'",
253 temp_code_point
254 )
255 .to_string(),
256 )))
257 }
258 }
259 }
260
261 match u32::from_str_radix(&temp_code_point, 16).map(std::char::from_u32) {
263 Ok(Some(unicode_char)) => res.push(unicode_char),
264 _ => {
265 return Err(Error::Unexpected(Info::Owned(
266 format_args!(
267 "{} is not a valid unicode code point",
268 temp_code_point
269 )
270 .to_string(),
271 )))
272 }
273 }
274 }
275 c => {
276 return Err(Error::Unexpected(Info::Owned(
277 format_args!("bad escaped char {:?}", c).to_string(),
278 )));
279 }
280 }
281 }
282 c => res.push(c),
283 }
284 }
285
286 Ok(res)
287}
288
289pub fn string<'a>(input: &mut TokenStream<'a>) -> StdParseResult<String, TokenStream<'a>> {
290 choice((
291 kind(T::StringValue).and_then(|tok| unquote_string(tok.value)),
292 kind(T::BlockString).and_then(|tok| unquote_block_string(tok.value)),
293 ))
294 .parse_stream(input)
295 .into_result()
296}
297
298pub fn string_value<'a, S>(
299 input: &mut TokenStream<'a>,
300) -> StdParseResult<Value<'a, S>, TokenStream<'a>>
301where
302 S: Text<'a>,
303{
304 kind(T::StringValue)
305 .and_then(|tok| unquote_string(tok.value))
306 .map(Value::String)
307 .parse_stream(input)
308 .into_result()
309}
310
311pub fn block_string_value<'a, S>(
312 input: &mut TokenStream<'a>,
313) -> StdParseResult<Value<'a, S>, TokenStream<'a>>
314where
315 S: Text<'a>,
316{
317 kind(T::BlockString)
318 .and_then(|tok| unquote_block_string(tok.value))
319 .map(Value::String)
320 .parse_stream(input)
321 .into_result()
322}
323
324pub fn plain_value<'a, T>(
325 input: &mut TokenStream<'a>,
326) -> StdParseResult<Value<'a, T>, TokenStream<'a>>
327where
328 T: Text<'a>,
329{
330 ident("true")
331 .map(|_| Value::Boolean(true))
332 .or(ident("false").map(|_| Value::Boolean(false)))
333 .or(ident("null").map(|_| Value::Null))
334 .or(name::<'a, T>().map(Value::Enum))
335 .or(parser(int_value))
336 .or(parser(float_value))
337 .or(parser(string_value))
338 .or(parser(block_string_value))
339 .parse_stream(input)
340 .into_result()
341}
342
343pub fn value<'a, T>(input: &mut TokenStream<'a>) -> StdParseResult<Value<'a, T>, TokenStream<'a>>
344where
345 T: Text<'a>,
346{
347 parser(plain_value)
348 .or(punct("$").with(name::<'a, T>()).map(Value::Variable))
349 .or(punct("[")
350 .with(many(parser(value)))
351 .skip(punct("]"))
352 .map(Value::List))
353 .or(punct("{")
354 .with(many(name::<'a, T>().skip(punct(":")).and(parser(value))))
355 .skip(punct("}"))
356 .map(Value::Object))
357 .parse_stream(input)
358 .into_result()
359}
360
361pub fn default_value<'a, T>(
362 input: &mut TokenStream<'a>,
363) -> StdParseResult<Value<'a, T>, TokenStream<'a>>
364where
365 T: Text<'a>,
366{
367 parser(plain_value)
368 .or(punct("[")
369 .with(many(parser(default_value)))
370 .skip(punct("]"))
371 .map(Value::List))
372 .or(punct("{")
373 .with(many(
374 name::<'a, T>().skip(punct(":")).and(parser(default_value)),
375 ))
376 .skip(punct("}"))
377 .map(Value::Object))
378 .parse_stream(input)
379 .into_result()
380}
381
382pub fn parse_type<'a, T>(
383 input: &mut TokenStream<'a>,
384) -> StdParseResult<Type<'a, T>, TokenStream<'a>>
385where
386 T: Text<'a>,
387{
388 name::<'a, T>()
389 .map(Type::NamedType)
390 .or(punct("[")
391 .with(parser(parse_type))
392 .skip(punct("]"))
393 .map(Box::new)
394 .map(Type::ListType))
395 .and(optional(punct("!")).map(|v| v.is_some()))
396 .map(|(typ, strict)| {
397 if strict {
398 Type::NonNullType(Box::new(typ))
399 } else {
400 typ
401 }
402 })
403 .parse_stream(input)
404 .into_result()
405}
406
407#[cfg(test)]
408mod tests {
409 use super::unquote_block_string;
410 use super::unquote_string;
411 use super::Number;
412
413 #[test]
414 fn number_from_i32_and_to_i64_conversion() {
415 assert_eq!(Number::from(1).as_i64(), Some(1));
416 assert_eq!(Number::from(584).as_i64(), Some(584));
417 assert_eq!(
418 Number::from(i32::min_value()).as_i64(),
419 Some(i32::min_value() as i64)
420 );
421 assert_eq!(
422 Number::from(i32::max_value()).as_i64(),
423 Some(i32::max_value() as i64)
424 );
425 }
426
427 #[test]
428 fn unquote_unicode_string() {
429 assert_eq!(unquote_string(r#""\u0009""#).expect(""), "\u{0009}");
431 assert_eq!(unquote_string(r#""\u000A""#).expect(""), "\u{000A}");
432 assert_eq!(unquote_string(r#""\u000D""#).expect(""), "\u{000D}");
433 assert_eq!(unquote_string(r#""\u0020""#).expect(""), "\u{0020}");
434 assert_eq!(unquote_string(r#""\uFFFF""#).expect(""), "\u{FFFF}");
435
436 assert_eq!(
438 unquote_string(r#""\u0009 hello \u000A there""#).expect(""),
439 "\u{0009} hello \u{000A} there"
440 );
441 }
442
443 #[test]
444 fn block_string_leading_and_trailing_empty_lines() {
445 let block = &triple_quote(" \n\n Hello,\n World!\n\n Yours,\n GraphQL.\n\n\n");
446 assert_eq!(
447 unquote_block_string(&block),
448 Result::Ok("Hello,\n World!\n\nYours,\n GraphQL.".to_string())
449 );
450 }
451
452 #[test]
453 fn block_string_indent() {
454 let block = &triple_quote("Hello \n\n Hello,\n World!\n");
455 assert_eq!(
456 unquote_block_string(&block),
457 Result::Ok("Hello \n\nHello,\n World!".to_string())
458 );
459 }
460
461 #[test]
462 fn block_string_escaping() {
463 let block = triple_quote(r#"\""""#);
464 assert_eq!(
465 unquote_block_string(&block),
466 Result::Ok("\"\"\"".to_string())
467 );
468 }
469
470 #[test]
471 fn block_string_empty() {
472 let block = triple_quote("");
473 assert_eq!(unquote_block_string(&block), Result::Ok("".to_string()));
474 let block = triple_quote(" \n\t\n");
475 assert_eq!(unquote_block_string(&block), Result::Ok("".to_string()));
476 }
477
478 fn triple_quote(input: &str) -> String {
479 return format!("\"\"\"{}\"\"\"", input);
480 }
481}