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