1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use crate::ast::*;
19
use crate::combinator::{choice, optional, zero_or_more};
20
use crate::parser::json::object_value;
21
use crate::parser::primitives::*;
22
use crate::parser::string::escape_char;
23
use crate::parser::{template, ParseError, ParseErrorKind, ParseResult};
24
use crate::reader::Reader;
25

            
26
25720
pub fn multiline_string(reader: &mut Reader) -> ParseResult<MultilineString> {
27
25720
    try_literal("```", reader)?;
28

            
29
765
    choice(&[json_text, xml_text, graphql, plain_text], reader)
30
}
31

            
32
1400
fn text(lang: &str, reader: &mut Reader) -> ParseResult<(Text, Vec<MultilineStringAttribute>)> {
33
1400
    try_literal(lang, reader)?;
34
240
    drop(try_literal(",", reader));
35
240
    let attributes = multiline_string_attributes(reader)?;
36
240
    let escape = attributes.contains(&MultilineStringAttribute::Escape);
37
240
    let space = zero_or_more_spaces(reader)?;
38
240
    let newline = newline(reader)?;
39
240
    let value = multiline_string_value(reader, escape)?;
40
240
    Ok((
41
240
        Text {
42
240
            space,
43
240
            newline,
44
240
            value,
45
240
        },
46
240
        attributes,
47
240
    ))
48
}
49

            
50
765
fn json_text(reader: &mut Reader) -> ParseResult<MultilineString> {
51
765
    let (text, attributes) = text("json", reader)?;
52
130
    let kind = MultilineStringKind::Json(text);
53
130
    Ok(MultilineString { kind, attributes })
54
}
55

            
56
635
fn xml_text(reader: &mut Reader) -> ParseResult<MultilineString> {
57
635
    let (text, attributes) = text("xml", reader)?;
58
110
    let kind = MultilineStringKind::Xml(text);
59
110
    Ok(MultilineString { kind, attributes })
60
}
61

            
62
525
fn graphql(reader: &mut Reader) -> ParseResult<MultilineString> {
63
525
    try_literal("graphql", reader)?;
64
190
    let space = zero_or_more_spaces(reader)?;
65
190
    drop(try_literal(",", reader));
66
190
    let attributes = multiline_string_attributes(reader)?;
67
190
    let newline = newline(reader)?;
68

            
69
190
    let mut chars = vec![];
70
190

            
71
190
    let start = reader.cursor();
72
15885
    while reader.peek_n(3) != "```" && !reader.is_eof() {
73
15735
        let pos = reader.cursor().pos;
74
15735
        let c = reader.read().unwrap();
75
15735
        chars.push((c, c.to_string(), pos));
76
15735
        if c == '\n' {
77
1570
            let end = reader.cursor();
78
1570
            let variables = optional(graphql_variables, reader)?;
79
1570
            match variables {
80
1530
                None => continue,
81
40
                Some(variables) => {
82
40
                    literal("```", reader)?;
83

            
84
40
                    let encoded_string = template::EncodedString {
85
40
                        source_info: SourceInfo::new(start.pos, end.pos),
86
40
                        chars: chars.clone(),
87
40
                    };
88

            
89
40
                    let elements = template::templatize(encoded_string)?;
90
40
                    let template = Template {
91
40
                        delimiter: None,
92
40
                        elements,
93
40
                        source_info: SourceInfo::new(start.pos, end.pos),
94
40
                    };
95
40
                    let kind = MultilineStringKind::GraphQl(GraphQl {
96
40
                        space,
97
40
                        newline,
98
40
                        value: template,
99
40
                        variables: Some(variables),
100
40
                    });
101
40
                    let attributes = vec![];
102
40
                    return Ok(MultilineString { kind, attributes });
103
                }
104
            }
105
        }
106
    }
107
150
    let end = reader.cursor();
108
150
    literal("```", reader)?;
109

            
110
150
    let encoded_string = template::EncodedString {
111
150
        source_info: SourceInfo::new(start.pos, end.pos),
112
150
        chars,
113
150
    };
114

            
115
150
    let elements = template::templatize(encoded_string)?;
116
150
    let template = Template {
117
150
        delimiter: None,
118
150
        elements,
119
150
        source_info: SourceInfo::new(start.pos, end.pos),
120
150
    };
121
150

            
122
150
    let kind = MultilineStringKind::GraphQl(GraphQl {
123
150
        space,
124
150
        newline,
125
150
        value: template,
126
150
        variables: None,
127
150
    });
128
150
    Ok(MultilineString { kind, attributes })
129
}
130

            
131
765
fn multiline_string_attributes(reader: &mut Reader) -> ParseResult<Vec<MultilineStringAttribute>> {
132
765
    let mut attributes = vec![];
133
765
    zero_or_more_spaces(reader)?;
134

            
135
805
    while !reader.is_eof() && reader.peek() != Some('\n') && reader.peek() != Some('\r') {
136
45
        let pos = reader.cursor().pos;
137
45
        let attribute = reader
138
309
            .read_while(|c| c != ',' && c != '\r' && c != '\n')
139
45
            .trim()
140
45
            .to_string();
141
45
        if attribute == "escape" {
142
40
            attributes.push(MultilineStringAttribute::Escape);
143
40
        } else if attribute == "novariable" {
144
            attributes.push(MultilineStringAttribute::NoVariable);
145
        } else {
146
5
            let kind = ParseErrorKind::MultilineAttribute(attribute);
147
5
            return Err(ParseError {
148
5
                kind,
149
5
                pos,
150
5
                recoverable: false,
151
5
            });
152
        }
153
40
        drop(try_literal(",", reader));
154
    }
155
760
    Ok(attributes)
156
}
157

            
158
80
fn whitespace(reader: &mut Reader) -> ParseResult<Whitespace> {
159
80
    let start = reader.cursor();
160
80
    match reader.read() {
161
        None => Err(ParseError::new(start.pos, true, ParseErrorKind::Space)),
162
80
        Some(c) => {
163
80
            if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
164
40
                Ok(Whitespace {
165
40
                    value: c.to_string(),
166
40
                    source_info: SourceInfo::new(start.pos, reader.cursor().pos),
167
40
                })
168
            } else {
169
40
                Err(ParseError::new(start.pos, true, ParseErrorKind::Space))
170
            }
171
        }
172
    }
173
}
174

            
175
40
fn zero_or_more_whitespaces(reader: &mut Reader) -> ParseResult<Whitespace> {
176
40
    let start = reader.cursor();
177
40
    match zero_or_more(whitespace, reader) {
178
40
        Ok(v) => {
179
48
            let s = v.iter().map(|x| x.value.clone()).collect();
180
40
            Ok(Whitespace {
181
40
                value: s,
182
40
                source_info: SourceInfo::new(start.pos, reader.cursor().pos),
183
40
            })
184
        }
185
        Err(e) => Err(e),
186
    }
187
}
188

            
189
1570
fn graphql_variables(reader: &mut Reader) -> ParseResult<GraphQlVariables> {
190
1570
    try_literal("variables", reader)?;
191
40
    let space = zero_or_more_spaces(reader)?;
192
40
    let start = reader.cursor();
193
40
    let object = object_value(reader);
194
40
    let value = match object {
195
40
        Ok(obj) => obj,
196
        Err(_) => {
197
            return Err(ParseError::new(
198
                start.pos,
199
                false,
200
                ParseErrorKind::GraphQlVariables,
201
            ))
202
        }
203
    };
204
40
    let whitespace = zero_or_more_whitespaces(reader)?;
205
40
    Ok(GraphQlVariables {
206
40
        space,
207
40
        value,
208
40
        whitespace,
209
40
    })
210
}
211

            
212
335
fn plain_text(reader: &mut Reader) -> ParseResult<MultilineString> {
213
335
    let space = zero_or_more_spaces(reader)?;
214
335
    drop(try_literal(",", reader));
215
335
    let attributes = multiline_string_attributes(reader)?;
216
330
    let escape = attributes.contains(&MultilineStringAttribute::Escape);
217
330
    let newline = newline(reader)?;
218
330
    let value = multiline_string_value(reader, escape)?;
219
330
    let kind = MultilineStringKind::Text(Text {
220
330
        space,
221
330
        newline,
222
330
        value,
223
330
    });
224
330
    Ok(MultilineString { kind, attributes })
225
}
226

            
227
570
fn multiline_string_value(reader: &mut Reader, escape: bool) -> ParseResult<Template> {
228
570
    let mut chars = vec![];
229
570

            
230
570
    let start = reader.cursor();
231
82650
    while reader.peek_n(3) != "```" && !reader.is_eof() {
232
82080
        let pos = reader.cursor().pos;
233
82080
        let cursor = reader.cursor();
234
82080
        if escape {
235
735
            match escape_char(reader) {
236
40
                Ok(c) => {
237
40
                    let s = reader.read_from(cursor.index);
238
40
                    chars.push((c, s, pos));
239
                }
240
695
                Err(_) => {
241
695
                    let c = reader.read().unwrap();
242
695
                    chars.push((c, c.to_string(), pos));
243
                }
244
            }
245
81345
        } else {
246
81345
            let c = reader.read().unwrap();
247
81345
            chars.push((c, c.to_string(), pos));
248
        }
249
    }
250
570
    let end = reader.cursor();
251
570
    literal("```", reader)?;
252

            
253
570
    let encoded_string = template::EncodedString {
254
570
        source_info: SourceInfo::new(start.pos, end.pos),
255
570
        chars,
256
570
    };
257

            
258
570
    let elements = template::templatize(encoded_string)?;
259

            
260
570
    Ok(Template {
261
570
        delimiter: None,
262
570
        elements,
263
570
        source_info: SourceInfo::new(start.pos, end.pos),
264
570
    })
265
}
266

            
267
#[cfg(test)]
268
mod tests {
269
    use crate::reader::Pos;
270

            
271
    use super::*;
272

            
273
    #[test]
274
    fn test_multiline_string_text() {
275
        let mut reader = Reader::new("```\nline1\nline2\nline3\n```");
276
        assert_eq!(
277
            multiline_string(&mut reader).unwrap(),
278
            MultilineString {
279
                kind: MultilineStringKind::Text(Text {
280
                    space: Whitespace {
281
                        value: String::new(),
282
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
283
                    },
284
                    newline: Whitespace {
285
                        value: "\n".to_string(),
286
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(2, 1)),
287
                    },
288
                    value: Template {
289
                        delimiter: None,
290
                        elements: vec![TemplateElement::String {
291
                            value: "line1\nline2\nline3\n".to_string(),
292
                            encoded: "line1\nline2\nline3\n".to_string(),
293
                        }],
294
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
295
                    },
296
                }),
297
                attributes: vec![]
298
            }
299
        );
300

            
301
        let mut reader = Reader::new("```         \nline1\nline2\nline3\n```");
302
        assert_eq!(
303
            multiline_string(&mut reader).unwrap(),
304
            MultilineString {
305
                kind: MultilineStringKind::Text(Text {
306
                    space: Whitespace {
307
                        value: "         ".to_string(),
308
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 13)),
309
                    },
310
                    newline: Whitespace {
311
                        value: "\n".to_string(),
312
                        source_info: SourceInfo::new(Pos::new(1, 13), Pos::new(2, 1)),
313
                    },
314
                    value: Template {
315
                        delimiter: None,
316
                        elements: vec![TemplateElement::String {
317
                            value: "line1\nline2\nline3\n".to_string(),
318
                            encoded: "line1\nline2\nline3\n".to_string(),
319
                        }],
320
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
321
                    },
322
                }),
323
                attributes: vec![]
324
            }
325
        );
326
    }
327

            
328
    #[test]
329
    fn test_multiline_string_json() {
330
        let mut reader = Reader::new("```json\nline1\nline2\nline3\n```");
331
        assert_eq!(
332
            multiline_string(&mut reader).unwrap(),
333
            MultilineString {
334
                kind: MultilineStringKind::Json(Text {
335
                    space: Whitespace {
336
                        value: String::new(),
337
                        source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
338
                    },
339
                    newline: Whitespace {
340
                        value: "\n".to_string(),
341
                        source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(2, 1)),
342
                    },
343
                    value: Template {
344
                        delimiter: None,
345
                        elements: vec![TemplateElement::String {
346
                            value: "line1\nline2\nline3\n".to_string(),
347
                            encoded: "line1\nline2\nline3\n".to_string(),
348
                        }],
349
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
350
                    },
351
                }),
352
                attributes: vec![]
353
            }
354
        );
355

            
356
        let mut reader = Reader::new(
357
            r#"```json,escape
358
{
359
  "g_clef": "\u{1D11E}"
360
}
361
```"#,
362
        );
363
        assert_eq!(
364
            multiline_string(&mut reader).unwrap(),
365
            MultilineString {
366
                kind: MultilineStringKind::Json(Text {
367
                    space: Whitespace {
368
                        value: String::new(),
369
                        source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
370
                    },
371
                    newline: Whitespace {
372
                        value: "\n".to_string(),
373
                        source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(2, 1)),
374
                    },
375
                    value: Template {
376
                        delimiter: None,
377
                        elements: vec![TemplateElement::String {
378
                            value: "{\n  \"g_clef\": \"𝄞\"\n}\n".to_string(),
379
                            encoded: "{\n  \"g_clef\": \"\\u{1D11E}\"\n}\n".to_string(),
380
                        }],
381
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
382
                    },
383
                }),
384
                attributes: vec![MultilineStringAttribute::Escape]
385
            }
386
        );
387
    }
388

            
389
    #[test]
390
    fn test_multiline_string_graphql() {
391
        let mut reader = Reader::new("```graphql\nline1\nline2\nline3\n```");
392
        assert_eq!(
393
            multiline_string(&mut reader).unwrap(),
394
            MultilineString {
395
                kind: MultilineStringKind::GraphQl(GraphQl {
396
                    space: Whitespace {
397
                        value: String::new(),
398
                        source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 11)),
399
                    },
400
                    newline: Whitespace {
401
                        value: "\n".to_string(),
402
                        source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(2, 1)),
403
                    },
404
                    value: Template {
405
                        delimiter: None,
406
                        elements: vec![TemplateElement::String {
407
                            value: "line1\nline2\nline3\n".to_string(),
408
                            encoded: "line1\nline2\nline3\n".to_string(),
409
                        }],
410
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
411
                    },
412
                    variables: None,
413
                }),
414
                attributes: vec![]
415
            }
416
        );
417

            
418
        let mut reader = Reader::new("```graphql      \nline1\nline2\nline3\n```");
419
        assert_eq!(
420
            multiline_string(&mut reader).unwrap(),
421
            MultilineString {
422
                kind: MultilineStringKind::GraphQl(GraphQl {
423
                    space: Whitespace {
424
                        value: "      ".to_string(),
425
                        source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 17)),
426
                    },
427
                    newline: Whitespace {
428
                        value: "\n".to_string(),
429
                        source_info: SourceInfo::new(Pos::new(1, 17), Pos::new(2, 1)),
430
                    },
431
                    value: Template {
432
                        delimiter: None,
433
                        elements: vec![TemplateElement::String {
434
                            value: "line1\nline2\nline3\n".to_string(),
435
                            encoded: "line1\nline2\nline3\n".to_string(),
436
                        }],
437
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
438
                    },
439
                    variables: None,
440
                }),
441
                attributes: vec![]
442
            }
443
        );
444
    }
445

            
446
    #[test]
447
    fn test_multiline_string_failed() {
448
        let data = [
449
            "```hexaaa\nline1\nline2\nline3\n```",
450
            "```aaa\nline1\nline2\nline3\n```",
451
        ];
452

            
453
        for text in data.iter() {
454
            let mut reader = Reader::new(text);
455
            assert!(multiline_string(&mut reader).is_err());
456
        }
457
    }
458

            
459
    #[test]
460
    fn test_multiline_string_empty() {
461
        let mut reader = Reader::new("```\n```");
462
        assert_eq!(
463
            multiline_string(&mut reader).unwrap(),
464
            MultilineString {
465
                kind: MultilineStringKind::Text(Text {
466
                    space: Whitespace {
467
                        value: String::new(),
468
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
469
                    },
470
                    newline: Whitespace {
471
                        value: "\n".to_string(),
472
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(2, 1)),
473
                    },
474
                    value: Template {
475
                        delimiter: None,
476
                        elements: vec![],
477
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
478
                    },
479
                }),
480
                attributes: vec![]
481
            }
482
        );
483
        let mut reader = Reader::new("```\r\n```");
484
        assert_eq!(
485
            multiline_string(&mut reader).unwrap(),
486
            MultilineString {
487
                kind: MultilineStringKind::Text(Text {
488
                    space: Whitespace {
489
                        value: String::new(),
490
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
491
                    },
492
                    newline: Whitespace {
493
                        value: "\r\n".to_string(),
494
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(2, 1)),
495
                    },
496
                    value: Template {
497
                        delimiter: None,
498
                        elements: vec![],
499
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
500
                    },
501
                }),
502
                attributes: vec![]
503
            }
504
        );
505
    }
506

            
507
    #[test]
508
    fn test_multiline_string_hello_error() {
509
        let mut reader = Reader::new("```Hello World!```");
510
        let error = multiline_string(&mut reader).unwrap_err();
511
        assert_eq!(error.pos, Pos::new(1, 4));
512
        assert_eq!(
513
            error.kind,
514
            ParseErrorKind::MultilineAttribute("Hello World!```".to_string())
515
        );
516
    }
517

            
518
    #[test]
519
    fn test_multiline_string_csv() {
520
        let mut reader = Reader::new("```\nline1\nline2\nline3\n```");
521
        assert_eq!(
522
            multiline_string(&mut reader).unwrap(),
523
            MultilineString {
524
                kind: MultilineStringKind::Text(Text {
525
                    space: Whitespace {
526
                        value: String::new(),
527
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
528
                    },
529
                    newline: Whitespace {
530
                        value: "\n".to_string(),
531
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(2, 1)),
532
                    },
533
                    value: Template {
534
                        delimiter: None,
535
                        elements: vec![TemplateElement::String {
536
                            value: "line1\nline2\nline3\n".to_string(),
537
                            encoded: "line1\nline2\nline3\n".to_string(),
538
                        }],
539
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(5, 1)),
540
                    },
541
                }),
542
                attributes: vec![]
543
            }
544
        );
545
    }
546

            
547
    #[test]
548
    fn test_multiline_string_one_empty_line() {
549
        let mut reader = Reader::new("```\n\n```");
550
        assert_eq!(
551
            multiline_string(&mut reader).unwrap(),
552
            MultilineString {
553
                kind: MultilineStringKind::Text(Text {
554
                    space: Whitespace {
555
                        value: String::new(),
556
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
557
                    },
558
                    newline: Whitespace {
559
                        value: "\n".to_string(),
560
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(2, 1)),
561
                    },
562
                    value: Template {
563
                        delimiter: None,
564
                        elements: vec![TemplateElement::String {
565
                            value: "\n".to_string(),
566
                            encoded: "\n".to_string(),
567
                        }],
568
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(3, 1)),
569
                    },
570
                }),
571
                attributes: vec![]
572
            }
573
        );
574

            
575
        // One cr
576
        let mut reader = Reader::new("```\n\r\n````");
577
        assert_eq!(
578
            multiline_string(&mut reader).unwrap(),
579
            MultilineString {
580
                kind: MultilineStringKind::Text(Text {
581
                    space: Whitespace {
582
                        value: String::new(),
583
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
584
                    },
585
                    newline: Whitespace {
586
                        value: "\n".to_string(),
587
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(2, 1)),
588
                    },
589
                    value: Template {
590
                        delimiter: None,
591
                        elements: vec![TemplateElement::String {
592
                            value: "\r\n".to_string(),
593
                            encoded: "\r\n".to_string(),
594
                        }],
595
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(3, 1)),
596
                    },
597
                }),
598
                attributes: vec![]
599
            }
600
        );
601
    }
602

            
603
    #[test]
604
    fn test_multiline_string_attributes() {
605
        let mut reader = Reader::new("escape\n```");
606
        assert_eq!(
607
            multiline_string_attributes(&mut reader).unwrap(),
608
            vec![MultilineStringAttribute::Escape]
609
        );
610
        assert_eq!(reader.cursor().index, 6);
611

            
612
        let mut reader = Reader::new("\n```");
613
        assert_eq!(multiline_string_attributes(&mut reader).unwrap(), vec![]);
614
        assert_eq!(reader.cursor().index, 0);
615

            
616
        let mut reader = Reader::new("\r\n```");
617
        assert_eq!(multiline_string_attributes(&mut reader).unwrap(), vec![]);
618
        assert_eq!(reader.cursor().index, 0);
619

            
620
        let mut reader = Reader::new("toto\n```");
621
        let error = multiline_string_attributes(&mut reader).unwrap_err();
622
        assert_eq!(
623
            error.kind,
624
            ParseErrorKind::MultilineAttribute("toto".to_string())
625
        );
626
        assert_eq!(error.pos, Pos::new(1, 1));
627

            
628
        let mut reader = Reader::new(",escape\n```");
629
        let error = multiline_string_attributes(&mut reader).unwrap_err();
630
        assert_eq!(
631
            error.kind,
632
            ParseErrorKind::MultilineAttribute(String::new())
633
        );
634
        assert_eq!(error.pos, Pos::new(1, 1));
635
    }
636

            
637
    #[test]
638
    fn test_multiline_string_escape() {
639
        let mut reader = Reader::new("```escape\n\\t\n```");
640
        assert_eq!(
641
            multiline_string(&mut reader).unwrap(),
642
            MultilineString {
643
                kind: MultilineStringKind::Text(Text {
644
                    space: Whitespace {
645
                        value: String::new(),
646
                        source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 4)),
647
                    },
648
                    newline: Whitespace {
649
                        value: "\n".to_string(),
650
                        source_info: SourceInfo::new(Pos::new(1, 10), Pos::new(2, 1)),
651
                    },
652
                    value: Template {
653
                        delimiter: None,
654
                        elements: vec![TemplateElement::String {
655
                            value: "\t\n".to_string(),
656
                            encoded: "\\t\n".to_string(),
657
                        }],
658
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(3, 1)),
659
                    },
660
                }),
661
                attributes: vec![MultilineStringAttribute::Escape]
662
            }
663
        );
664
    }
665

            
666
    #[test]
667
    fn test_multiline_string_error() {
668
        let mut reader = Reader::new("xxx");
669
        let error = multiline_string(&mut reader).err().unwrap();
670
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
671
        assert_eq!(
672
            error.kind,
673
            ParseErrorKind::Expecting {
674
                value: "```".to_string()
675
            }
676
        );
677
        assert!(error.recoverable);
678

            
679
        let mut reader = Reader::new("```\nxxx");
680
        let error = multiline_string(&mut reader).err().unwrap();
681
        assert_eq!(error.pos, Pos { line: 2, column: 4 });
682
        assert_eq!(
683
            error.kind,
684
            ParseErrorKind::Expecting {
685
                value: "```".to_string()
686
            }
687
        );
688
        assert!(!error.recoverable);
689

            
690
        let mut reader = Reader::new("```xxx");
691
        let error = multiline_string(&mut reader).err().unwrap();
692
        assert_eq!(error.pos, Pos { line: 1, column: 4 });
693
        assert_eq!(
694
            error.kind,
695
            ParseErrorKind::MultilineAttribute("xxx".to_string())
696
        );
697
        assert!(!error.recoverable);
698
    }
699

            
700
    #[test]
701
    fn test_multiline_string_value() {
702
        let mut reader = Reader::new("```");
703
        assert_eq!(
704
            multiline_string_value(&mut reader, false).unwrap(),
705
            Template {
706
                delimiter: None,
707
                elements: vec![],
708
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
709
            }
710
        );
711
        assert_eq!(reader.cursor().index, 3);
712

            
713
        let mut reader = Reader::new("hello```");
714
        assert_eq!(
715
            multiline_string_value(&mut reader, false).unwrap(),
716
            Template {
717
                delimiter: None,
718
                elements: vec![TemplateElement::String {
719
                    value: "hello".to_string(),
720
                    encoded: "hello".to_string(),
721
                }],
722
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6)),
723
            }
724
        );
725
        assert_eq!(reader.cursor().index, 8);
726
    }
727

            
728
    #[test]
729
    fn test_multiline_string_graphql_with_variables() {
730
        let mut reader = Reader::new(
731
            r#"```graphql
732
query Human($name: String!) {
733
  human(name: $name) {
734
    name
735
    height(unit: FOOT)
736
}
737

            
738
variables {
739
  "name": "Han Solo"
740
}
741
```"#,
742
        );
743

            
744
        assert_eq!(
745
            multiline_string(&mut reader).unwrap(),
746
            MultilineString {
747
            kind: MultilineStringKind::GraphQl(GraphQl {
748
                space: Whitespace {
749
                    value: String::new(),
750
                    source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 11)),
751
                },
752
                newline: Whitespace {
753
                    value: "\n".to_string(),
754
                    source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(2, 1)),
755
                },
756
                value: Template {
757
                    delimiter: None,
758
                    elements: vec![TemplateElement::String {
759
                        value: "query Human($name: String!) {\n  human(name: $name) {\n    name\n    height(unit: FOOT)\n}\n\n".to_string(),
760
                        encoded:
761
                            "query Human($name: String!) {\n  human(name: $name) {\n    name\n    height(unit: FOOT)\n}\n\n".to_string()
762
                    }],
763
                    source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(8, 1)),
764
                },
765
                variables: Some(GraphQlVariables {
766
                    space: Whitespace {
767
                        value: " ".to_string(),
768
                        source_info: SourceInfo::new(Pos::new(8, 10), Pos::new(8, 11)),
769
                    },
770
                    value: JsonValue::Object {
771
                        space0: "\n  ".to_string(),
772
                        elements: vec![JsonObjectElement {
773
                            space0: String::new(),
774
                            name: Template {
775
                                delimiter: Some('"'),
776
                                elements: vec![
777
                                    TemplateElement::String {
778
                                        value: "name".to_string(),
779
                                        encoded: "name".to_string()
780
                                    }
781
                                ],
782
                                source_info: SourceInfo::new(Pos::new(9, 4), Pos::new(9, 8))
783
                            },
784
                            space1: String::new(),
785
                            space2: " ".to_string(),
786
                            value: JsonValue::String(Template {
787
                                delimiter: Some('"'),
788
                                elements: vec![
789
                                    TemplateElement::String {
790
                                        value: "Han Solo".to_string(),
791
                                        encoded: "Han Solo".to_string()
792
                                    }
793
                                ],
794
                                source_info: SourceInfo::new(Pos::new(9, 12), Pos::new(9, 20))
795
                            }),
796
                            space3: "\n".to_string()
797
                        }]
798
                    },
799
                    whitespace: Whitespace {
800
                        value: "\n".to_string(),
801
                        source_info: SourceInfo::new(Pos::new(10, 2), Pos::new(11, 1))
802
                    }
803
                })
804
            }), attributes: vec![]}
805
        );
806
    }
807

            
808
    #[test]
809
    fn test_multiline_string_graphql_with_variables_error() {
810
        let mut reader = Reader::new(
811
            r#"```graphql
812
query Human($name: String!) {
813
  human(name: $name) {
814
    name
815
    height(unit: FOOT)
816
}
817

            
818
variables
819
```"#,
820
        );
821

            
822
        let error = multiline_string(&mut reader).err().unwrap();
823
        assert_eq!(
824
            error,
825
            ParseError::new(Pos::new(8, 10), false, ParseErrorKind::GraphQlVariables)
826
        );
827
    }
828

            
829
    #[test]
830
    fn test_multiline_string_graphql_with_variables_not_an_object() {
831
        let mut reader = Reader::new(
832
            r#"```graphql
833
query Human($name: String!) {
834
  human(name: $name) {
835
    name
836
    height(unit: FOOT)
837
}
838

            
839
variables [
840
  "one",
841
  "two",
842
  "three"
843
]
844
```"#,
845
        );
846

            
847
        let error = multiline_string(&mut reader).err().unwrap();
848
        assert_eq!(
849
            error,
850
            ParseError::new(Pos::new(8, 11), false, ParseErrorKind::GraphQlVariables)
851
        );
852
    }
853
}