1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 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
    GraphQl, GraphQlVariables, MultilineString, MultilineStringAttribute, MultilineStringKind,
20
    SourceInfo, Template, Text, Whitespace,
21
};
22
use crate::combinator::{choice, optional, zero_or_more};
23
use crate::parser::json::object_value;
24
use crate::parser::primitives::{literal, newline, try_literal, zero_or_more_spaces};
25
use crate::parser::string::escape_char;
26
use crate::parser::{template, ParseError, ParseErrorKind, ParseResult};
27
use crate::reader::Reader;
28

            
29
27815
pub fn multiline_string(reader: &mut Reader) -> ParseResult<MultilineString> {
30
27815
    try_literal("```", reader)?;
31

            
32
825
    choice(&[json_text, xml_text, graphql, plain_text], reader)
33
}
34

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

            
53
825
fn json_text(reader: &mut Reader) -> ParseResult<MultilineString> {
54
825
    let (text, attributes) = text("json", reader)?;
55
145
    let kind = MultilineStringKind::Json(text);
56
145
    Ok(MultilineString { kind, attributes })
57
}
58

            
59
680
fn xml_text(reader: &mut Reader) -> ParseResult<MultilineString> {
60
680
    let (text, attributes) = text("xml", reader)?;
61
125
    let kind = MultilineStringKind::Xml(text);
62
125
    Ok(MultilineString { kind, attributes })
63
}
64

            
65
555
fn graphql(reader: &mut Reader) -> ParseResult<MultilineString> {
66
555
    try_literal("graphql", reader)?;
67
205
    let space = zero_or_more_spaces(reader)?;
68
205
    drop(try_literal(",", reader));
69
205
    let attributes = multiline_string_attributes(reader)?;
70
205
    let newline = newline(reader)?;
71

            
72
205
    let mut chars = vec![];
73
205

            
74
205
    let start = reader.cursor();
75
17250
    while reader.peek_n(3) != "```" && !reader.is_eof() {
76
17085
        let pos = reader.cursor().pos;
77
17085
        let c = reader.read().unwrap();
78
17085
        chars.push((c, c.to_string(), pos));
79
17085
        if c == '\n' {
80
1705
            let end = reader.cursor();
81
1705
            let variables = optional(graphql_variables, reader)?;
82
1705
            match variables {
83
1665
                None => continue,
84
40
                Some(variables) => {
85
40
                    literal("```", reader)?;
86

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

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

            
113
165
    let encoded_string = template::EncodedString {
114
165
        source_info: SourceInfo::new(start.pos, end.pos),
115
165
        chars,
116
165
    };
117

            
118
165
    let elements = template::templatize(encoded_string)?;
119
165
    let template = Template {
120
165
        delimiter: None,
121
165
        elements,
122
165
        source_info: SourceInfo::new(start.pos, end.pos),
123
165
    };
124
165

            
125
165
    let kind = MultilineStringKind::GraphQl(GraphQl {
126
165
        space,
127
165
        newline,
128
165
        value: template,
129
165
        variables: None,
130
165
    });
131
165
    Ok(MultilineString { kind, attributes })
132
}
133

            
134
825
fn multiline_string_attributes(reader: &mut Reader) -> ParseResult<Vec<MultilineStringAttribute>> {
135
825
    let mut attributes = vec![];
136
825
    zero_or_more_spaces(reader)?;
137

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

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

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

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

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

            
230
615
fn multiline_string_value(reader: &mut Reader, escape: bool) -> ParseResult<Template> {
231
615
    let mut chars = vec![];
232
615

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

            
256
615
    let encoded_string = template::EncodedString {
257
615
        source_info: SourceInfo::new(start.pos, end.pos),
258
615
        chars,
259
615
    };
260

            
261
615
    let elements = template::templatize(encoded_string)?;
262

            
263
615
    Ok(Template {
264
615
        delimiter: None,
265
615
        elements,
266
615
        source_info: SourceInfo::new(start.pos, end.pos),
267
615
    })
268
}
269

            
270
#[cfg(test)]
271
mod tests {
272
    use crate::ast::{JsonObjectElement, JsonValue, TemplateElement};
273
    use crate::reader::Pos;
274

            
275
    use super::*;
276

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

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

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

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

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

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

            
450
    #[test]
451
    fn test_multiline_string_failed() {
452
        let data = [
453
            "```hexaaa\nline1\nline2\nline3\n```",
454
            "```aaa\nline1\nline2\nline3\n```",
455
        ];
456

            
457
        for text in data.iter() {
458
            let mut reader = Reader::new(text);
459
            assert!(multiline_string(&mut reader).is_err());
460
        }
461
    }
462

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

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

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

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

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

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

            
616
        let mut reader = Reader::new("\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("\r\n```");
621
        assert_eq!(multiline_string_attributes(&mut reader).unwrap(), vec![]);
622
        assert_eq!(reader.cursor().index, 0);
623

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

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

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

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

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

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

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

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

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

            
742
variables {
743
  "name": "Han Solo"
744
}
745
```"#,
746
        );
747

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

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

            
822
variables
823
```"#,
824
        );
825

            
826
        let error = multiline_string(&mut reader).err().unwrap();
827
        assert_eq!(
828
            error,
829
            ParseError::new(Pos::new(8, 10), false, ParseErrorKind::GraphQlVariables)
830
        );
831
    }
832

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

            
843
variables [
844
  "one",
845
  "two",
846
  "three"
847
]
848
```"#,
849
        );
850

            
851
        let error = multiline_string(&mut reader).err().unwrap();
852
        assert_eq!(
853
            error,
854
            ParseError::new(Pos::new(8, 11), false, ParseErrorKind::GraphQlVariables)
855
        );
856
    }
857
}