Add lang identifier to multiline.

This commit is contained in:
jcamiel 2022-11-15 12:00:29 +01:00
parent f3bf0b7618
commit 3b3483a997
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
25 changed files with 886 additions and 351 deletions

View File

@ -0,0 +1,7 @@
error: Parsing multine
--> tests_error_parser/multiline.hurl:2:4
|
2 | ```foo
| ^ the multiline is not valid
|

View File

@ -0,0 +1,2 @@
2

View File

@ -0,0 +1,6 @@
GET http://localhost:8000/invalid-multiline
```foo
bar
baz
```
HTTP 200

View File

@ -1,8 +1,8 @@
error: Assert body value
--> tests_failed/assert_newline.hurl:9:4
--> tests_failed/assert_newline.hurl:10:1
|
9 | ```<p>Hello</p>
| ^ actual value is <<p>Hello</p>
10 | <p>Hello</p>
| ^ actual value is <<p>Hello</p>
>
|

View File

@ -6,7 +6,8 @@
<span class="line"></span><span class="comment"># It should produce an assert error</span>
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/error-assert-newline</span></span>
</span><span class="response"><span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="raw"><span class="line">```&lt;p&gt;Hello&lt;/p&gt;</span>
<span class="raw"><span class="line">```</span>
<span class="line">&lt;p&gt;Hello&lt;/p&gt;</span>
<span class="line">```</span></span>
</span></span><span class="line"></span>
<span class="line"></span><span class="comment"># TODO</span>

View File

@ -6,7 +6,8 @@
# It should produce an assert error
GET http://localhost:8000/error-assert-newline
HTTP 200
```<p>Hello</p>
```
<p>Hello</p>
```
# TODO

View File

@ -1,15 +1,80 @@
<pre><code class="language-hurl"><span class="hurl-entry"><span class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines</span></span>
</span><span class="response"><span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<pre><code class="language-hurl"><span class="hurl-entry"><span class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines/plain-text</span></span>
</span><span class="response"><span class="line"></span>
<span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="line section-header">[Asserts]</span>
<span class="line"><span class="query-type">body</span> <span class="predicate-type">==</span> <span class="string">"line1\nline2\nline3\n"</span></span>
<span class="line"><span class="query-type">body</span> <span class="predicate-type">==</span> <span class="raw"><span class="line">```line1</span>
<span class="line">line2</span>
<span class="line">line3</span>
<span class="line">```</span></span></span>
<span class="line"><span class="query-type">body</span> <span class="predicate-type">==</span> <span class="raw"><span class="line">```</span>
<span class="line">line1</span>
<span class="line">line2</span>
<span class="line">line3</span>
<span class="line">```</span></span></span>
</span></span><span class="line"></span>
</code></pre>
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
<span class="line"></span>
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines/base64</span></span>
</span><span class="response"><span class="line"></span>
<span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="raw"><span class="line">```base64</span>
<span class="line">TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwg</span>
<span class="line">c2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu</span>
<span class="line">YSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0</span>
<span class="line">aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1</span>
<span class="line">YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2</span>
<span class="line">ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRl</span>
<span class="line">dXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1</span>
<span class="line">aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==</span>
<span class="line">```</span></span>
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
<span class="line"></span>
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines/hex</span></span>
</span><span class="response"><span class="line"></span>
<span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="raw"><span class="line">```hex</span>
<span class="line">039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81</span>
<span class="line">```</span></span>
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
<span class="line"></span>
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines/json</span></span>
</span><span class="response"><span class="line"></span>
<span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="raw"><span class="line">```json</span>
<span class="line">{</span>
<span class="line"> "foo": "bar"</span>
<span class="line"> "baz": 123456</span>
<span class="line">}</span>
<span class="line">```</span></span>
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
<span class="line"></span>
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines/xml</span></span>
</span><span class="response"><span class="line"></span>
<span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="raw"><span class="line">```xml</span>
<span class="line">&lt;?xml version="1.0"?&gt;</span>
<span class="line">&lt;catalog&gt;</span>
<span class="line"> &lt;book id="bk101"&gt;</span>
<span class="line"> &lt;author&gt;Gambardella, Matthew&lt;/author&gt;</span>
<span class="line"> &lt;title&gt;XML Developer's Guide&lt;/title&gt;</span>
<span class="line"> &lt;genre&gt;Computer&lt;/genre&gt;</span>
<span class="line"> &lt;price&gt;44.95&lt;/price&gt;</span>
<span class="line"> &lt;publish_date&gt;2000-10-01&lt;/publish_date&gt;</span>
<span class="line"> &lt;description&gt;An in-depth look at creating applications</span>
<span class="line"> with XML.&lt;/description&gt;</span>
<span class="line"> &lt;/book&gt;</span>
<span class="line">&lt;/catalog&gt;</span>
<span class="line">```</span></span>
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
<span class="line"></span>
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/multilines/graphql</span></span>
</span><span class="response"><span class="line"></span>
<span class="line"><span class="version">HTTP</span> <span class="number">200</span></span>
<span class="raw"><span class="line">```graphql</span>
<span class="line">{</span>
<span class="line"> hero {</span>
<span class="line"> name</span>
<span class="line"> # Queries can have comments!</span>
<span class="line"> friends {</span>
<span class="line"> name</span>
<span class="line"> }</span>
<span class="line"> }</span>
<span class="line">}</span>
<span class="line">```</span></span>
</span></span></code></pre>

View File

@ -1,14 +1,79 @@
GET http://localhost:8000/multilines
GET http://localhost:8000/multilines/plain-text
HTTP 200
[Asserts]
body == "line1\nline2\nline3\n"
body == ```line1
line2
line3
```
body == ```
line1
line2
line3
```
GET http://localhost:8000/multilines/base64
HTTP 200
```base64
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwg
c2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu
YSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0
aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1
YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2
ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRl
dXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1
aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==
```
GET http://localhost:8000/multilines/hex
HTTP 200
```hex
039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81
```
GET http://localhost:8000/multilines/json
HTTP 200
```json
{
"foo": "bar"
"baz": 123456
}
```
GET http://localhost:8000/multilines/xml
HTTP 200
```xml
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
</catalog>
```
GET http://localhost:8000/multilines/graphql
HTTP 200
```graphql
{
hero {
name
# Queries can have comments!
friends {
name
}
}
}
```

View File

@ -1 +1 @@
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/multilines"},"response":{"status":200,"asserts":[{"query":{"type":"body"},"predicate":{"type":"equal","value":"line1\nline2\nline3\n"}},{"query":{"type":"body"},"predicate":{"type":"equal","value":"line1\nline2\nline3\n"}},{"query":{"type":"body"},"predicate":{"type":"equal","value":"line1\nline2\nline3\n"}}]}}]}
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/multilines/plain-text"},"response":{"status":200,"asserts":[{"query":{"type":"body"},"predicate":{"type":"equal","value":"line1\nline2\nline3\n"}},{"query":{"type":"body"},"predicate":{"type":"equal","value":"line1\nline2\nline3\n"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/multilines/base64"},"response":{"status":200,"body":{"type":"base64","value":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwg\nc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu\nYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0\naW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1\nYXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2\nZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRl\ndXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1\naSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==\n"}}},{"request":{"method":"GET","url":"http://localhost:8000/multilines/hex"},"response":{"status":200,"body":{"type":"hex","value":"039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81\n"}}},{"request":{"method":"GET","url":"http://localhost:8000/multilines/json"},"response":{"status":200,"body":{"type":"json","value":"{\n \"foo\": \"bar\"\n \"baz\": 123456\n}\n"}}},{"request":{"method":"GET","url":"http://localhost:8000/multilines/xml"},"response":{"status":200,"body":{"type":"xml","value":"<?xml version=\"1.0\"?>\n<catalog>\n <book id=\"bk101\">\n <author>Gambardella, Matthew</author>\n <title>XML Developer's Guide</title>\n <genre>Computer</genre>\n <price>44.95</price>\n <publish_date>2000-10-01</publish_date>\n <description>An in-depth look at creating applications\n with XML.</description>\n </book>\n</catalog>\n"}}},{"request":{"method":"GET","url":"http://localhost:8000/multilines/graphql"},"response":{"status":200,"body":{"type":"graphql","value":"{\n hero {\n name\n # Queries can have comments!\n friends {\n name\n }\n }\n}\n"}}}]}

View File

@ -1,6 +1,68 @@
from app import app
@app.route("/multilines")
def multilines():
@app.route("/multilines/plain-text")
def multilines_plain_text():
return "line1\nline2\nline3\n"
@app.route("/multilines/base64")
def multilines_base64():
return """\
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwg
c2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu
YSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0
aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1
YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2
ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRl
dXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1
aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==
"""
@app.route("/multilines/json")
def multilines_json():
return """\
{
"foo": "bar"
"baz": 123456
}
"""
@app.route("/multilines/hex")
def multilines_hex():
return "039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81\n"
@app.route("/multilines/xml")
def multilines_xml():
return """\
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
</catalog>
"""
@app.route("/multilines/graphql")
def multilines_graphql():
return """\
{
hero {
name
# Queries can have comments!
friends {
name
}
}
}
"""

View File

@ -482,10 +482,26 @@ pub enum PredicateFuncValue {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RawString {
pub newline: Whitespace,
pub lang: Option<Lang>,
pub value: Template,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Lang {
pub value: Option<LangValue>,
pub space: Whitespace,
pub newline: Whitespace,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LangValue {
Base64,
Hex,
Json,
Xml,
GraphQl,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Base64 {
pub space0: Whitespace,

View File

@ -128,6 +128,19 @@ impl fmt::Display for CookieAttribute {
}
}
impl fmt::Display for LangValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
LangValue::Base64 => "base64",
LangValue::Hex => "hex",
LangValue::Json => "json",
LangValue::Xml => "xml",
LangValue::GraphQl => "graphql",
};
write!(f, "{}", s)
}
}
impl fmt::Display for Hex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(

View File

@ -58,6 +58,7 @@ impl Error for parser::Error {
ParseError::InvalidCookieAttribute { .. } => "Parsing cookie attribute".to_string(),
ParseError::OddNumberOfHexDigits { .. } => "Parsing hex bytearray".to_string(),
ParseError::UrlIllegalCharacter(_) => "Parsing URL".to_string(),
ParseError::Multiline => "Parsing multine".to_string(),
_ => format!("{:?}", self),
}
}
@ -109,7 +110,9 @@ impl Error for parser::Error {
"expecting an even number of hex digits".to_string()
}
ParseError::UrlIllegalCharacter(c) => format!("illegal character <{}>", c),
ParseError::Multiline => "the multiline is not valid".to_string(),
_ => format!("{:?}", self),
}
}
}

View File

@ -859,7 +859,12 @@ impl Htmlable for RawString {
let mut buffer = "".to_string();
buffer.push_str("<span class=\"raw\">");
let mut s = "```".to_string();
s.push_str(self.newline.value.as_str());
if let Some(lang) = &self.lang {
if let Some(lang_value) = &lang.value {
s.push_str(format!("{}", lang_value).as_str());
}
s.push('\n');
}
s.push_str(self.value.to_string().as_str());
s.push_str("```");
buffer.push_str(multilines(s).as_str());
@ -1073,10 +1078,7 @@ mod tests {
fn test_raw_string() {
// ``````
let raw_string = RawString {
newline: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(0, 0, 0, 0),
},
lang: None,
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
@ -1093,10 +1095,7 @@ mod tests {
// ```hello```
let raw_string = RawString {
newline: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(0, 0, 0, 0),
},
lang: None,
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
@ -1116,10 +1115,23 @@ mod tests {
// line2
// ```
let raw_string = RawString {
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(0, 0, 0, 0),
},
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo {
start: Pos { line: 1, column: 4 },
end: Pos { line: 1, column: 4 },
},
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo {
start: Pos { line: 1, column: 4 },
end: Pos { line: 2, column: 1 },
},
},
}),
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
@ -1133,27 +1145,6 @@ mod tests {
raw_string.to_html(),
"<span class=\"raw\"><span class=\"line\">```</span>\n<span class=\"line\">line1</span>\n<span class=\"line\">line2</span>\n<span class=\"line\">```</span></span>".to_string()
);
// ```Hello
// ```
let raw_string = RawString {
newline: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(0, 0, 0, 0),
},
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "Hello\n".to_string(),
encoded: "unused".to_string(),
}],
source_info: SourceInfo::new(0, 0, 0, 0),
},
};
assert_eq!(
raw_string.to_html(),
"<span class=\"raw\"><span class=\"line\">```Hello</span>\n<span class=\"line\">```</span></span>".to_string()
);
}
#[test]

View File

@ -16,6 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::multiline::multiline_string;
use super::combinators::*;
use super::json::parse as parse_json;
@ -25,10 +26,9 @@ use super::xml;
use super::ParseResult;
pub fn bytes(reader: &mut Reader) -> ParseResult<'static, Bytes> {
//let start = p.state.clone();
choice(
&[
raw_string_bytes,
multiline_string_bytes,
json_bytes,
xml_bytes,
base64_bytes,
@ -38,9 +38,6 @@ pub fn bytes(reader: &mut Reader) -> ParseResult<'static, Bytes> {
reader,
)
}
fn raw_string_bytes(reader: &mut Reader) -> ParseResult<'static, Bytes> {
raw_string(reader).map(Bytes::RawString)
}
fn xml_bytes(reader: &mut Reader) -> ParseResult<'static, Bytes> {
match xml::parse(reader) {
@ -68,9 +65,12 @@ fn hex_bytes(reader: &mut Reader) -> ParseResult<'static, Bytes> {
hex(reader).map(Bytes::Hex)
}
pub fn multiline_string_bytes(reader: &mut Reader) -> ParseResult<'static, Bytes> {
multiline_string(reader).map(Bytes::RawString)
}
#[cfg(test)]
mod tests {
use super::super::error::*;
use super::*;

View File

@ -61,6 +61,7 @@ pub enum ParseError {
OddNumberOfHexDigits,
UrlIllegalCharacter(char),
InvalidOption,
Multiline,
}
impl Error {

View File

@ -42,6 +42,7 @@ mod expr;
mod filename;
mod filter;
mod json;
mod multiline;
mod parsers;
mod predicate;
mod predicate_value;

View File

@ -0,0 +1,537 @@
/*
* Hurl (https://hurl.dev)
* Copyright (C) 2022 Orange
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
use super::combinators::*;
use super::reader::Reader;
use super::ParseResult;
use crate::ast::*;
use crate::parser::primitives::*;
use crate::parser::{template, Error, ParseError};
pub fn multiline_string(reader: &mut Reader) -> ParseResult<'static, RawString> {
try_literal("```", reader)?;
let save = reader.state.clone();
match choice(
&[
base64_lang,
hex_lang,
json_lang,
xml_lang,
graphql_lang,
undefined_lang,
],
reader,
) {
Ok(lang) => {
let value = multiline_string_value(reader)?;
Ok(RawString {
value,
lang: Some(lang),
})
}
Err(_) => {
reader.state = save;
let value = oneline_string_value(reader)?;
Ok(RawString { value, lang: None })
}
}
}
fn multiline_string_value(reader: &mut Reader) -> ParseResult<'static, Template> {
let mut chars = vec![];
let start = reader.state.pos.clone();
while !reader.remaining().starts_with("```") && !reader.is_eof() {
let pos = reader.state.pos.clone();
let c = reader.read().unwrap();
chars.push((c, c.to_string(), pos));
}
let end = reader.state.pos.clone();
literal("```", reader)?;
let encoded_string = template::EncodedString {
source_info: SourceInfo {
start: start.clone(),
end: end.clone(),
},
chars,
};
let elements = template::templatize(encoded_string)?;
Ok(Template {
quotes: false,
elements,
source_info: SourceInfo { start, end },
})
}
fn oneline_string_value(reader: &mut Reader) -> ParseResult<'static, Template> {
let mut chars = vec![];
let start = reader.state.pos.clone();
while !reader.remaining().starts_with("```") && !reader.is_eof() {
let pos = reader.state.pos.clone();
let c = reader.read().unwrap();
if c == '\n' {
return Err(Error {
pos: start,
recoverable: false,
inner: ParseError::Multiline,
});
}
chars.push((c, c.to_string(), pos));
}
let end = reader.state.pos.clone();
literal("```", reader)?;
let encoded_string = template::EncodedString {
source_info: SourceInfo {
start: start.clone(),
end: end.clone(),
},
chars,
};
let elements = template::templatize(encoded_string)?;
Ok(Template {
quotes: false,
elements,
source_info: SourceInfo { start, end },
})
}
fn undefined_lang(reader: &mut Reader) -> ParseResult<'static, Lang> {
let space = zero_or_more_spaces(reader)?;
let newline = newline(reader)?;
Ok(Lang {
value: None,
space,
newline,
})
}
fn base64_lang(reader: &mut Reader) -> ParseResult<'static, Lang> {
try_literal("base64", reader)?;
let space = zero_or_more_spaces(reader)?;
let newline = newline(reader)?;
Ok(Lang {
value: Some(LangValue::Base64),
space,
newline,
})
}
fn hex_lang(reader: &mut Reader) -> ParseResult<'static, Lang> {
try_literal("hex", reader)?;
let space = zero_or_more_spaces(reader)?;
let newline = newline(reader)?;
Ok(Lang {
value: Some(LangValue::Hex),
space,
newline,
})
}
fn json_lang(reader: &mut Reader) -> ParseResult<'static, Lang> {
try_literal("json", reader)?;
let space = zero_or_more_spaces(reader)?;
let newline = newline(reader)?;
Ok(Lang {
value: Some(LangValue::Json),
space,
newline,
})
}
fn xml_lang(reader: &mut Reader) -> ParseResult<'static, Lang> {
try_literal("xml", reader)?;
let space = zero_or_more_spaces(reader)?;
let newline = newline(reader)?;
Ok(Lang {
value: Some(LangValue::Xml),
space,
newline,
})
}
fn graphql_lang(reader: &mut Reader) -> ParseResult<'static, Lang> {
try_literal("graphql", reader)?;
let space = zero_or_more_spaces(reader)?;
let newline = newline(reader)?;
Ok(Lang {
value: Some(LangValue::GraphQl),
space,
newline,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base64_multiline_string_type() {
let mut reader = Reader::init("base64\nxxxx");
assert_eq!(
base64_lang(&mut reader).unwrap(),
Lang {
value: Some(LangValue::Base64),
space: Whitespace {
value: String::from(""),
source_info: SourceInfo::new(1, 7, 1, 7),
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(1, 7, 2, 1)
}
}
);
}
#[test]
fn test_base64_multiline_string_type_with_padding() {
let mut reader = Reader::init("base64 \nxxxx");
assert_eq!(
base64_lang(&mut reader).unwrap(),
Lang {
value: Some(LangValue::Base64),
space: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(1, 7, 1, 13),
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(1, 13, 2, 1)
}
}
);
}
#[test]
fn test_multiline_string_ok() {
let datas = [
(
"```\nline1\nline2\nline3\n```",
None,
"line1\nline2\nline3\n",
),
(
"``` \nline1\nline2\nline3\n```",
None,
"line1\nline2\nline3\n",
),
(
"```base64\nline1\nline2\nline3\n```",
Some(LangValue::Base64),
"line1\nline2\nline3\n",
),
(
"```hex\nline1\nline2\nline3\n```",
Some(LangValue::Hex),
"line1\nline2\nline3\n",
),
(
"```json\nline1\nline2\nline3\n```",
Some(LangValue::Json),
"line1\nline2\nline3\n",
),
(
"```graphql\nline1\nline2\nline3\n```",
Some(LangValue::GraphQl),
"line1\nline2\nline3\n",
),
(
"```graphql \nline1\nline2\nline3\n```",
Some(LangValue::GraphQl),
"line1\nline2\nline3\n",
),
];
for (text, lang, value) in datas.iter() {
let mut reader = Reader::init(text);
let multiline = multiline_string(&mut reader).unwrap();
assert_eq!(multiline.lang.unwrap().value, *lang);
assert_eq!(multiline.value.elements[0].to_string(), value.to_string())
}
}
#[test]
fn test_multiline_string_failed() {
let datas = [
"```hexaaa\nline1\nline2\nline3\n```",
"```aaa\nline1\nline2\nline3\n```",
];
for text in datas.iter() {
let mut reader = Reader::init(text);
assert!(multiline_string(&mut reader).is_err())
}
}
#[test]
fn test_multiline_string_empty() {
let mut reader = Reader::init("``````");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: None,
value: Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(1, 4, 1, 4),
},
}
);
let mut reader = Reader::init("```\n```");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(1, 4, 1, 4)
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(1, 4, 2, 1)
}
}),
value: Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(2, 1, 2, 1),
},
}
);
let mut reader = Reader::init("```\r\n```");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(1, 4, 1, 4)
},
newline: Whitespace {
value: "\r\n".to_string(),
source_info: SourceInfo::new(1, 4, 2, 1)
}
}),
value: Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(2, 1, 2, 1),
},
}
);
}
#[test]
fn test_raw_string_hello() {
let mut reader = Reader::init("```Hello World!```");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: None,
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "Hello World!".to_string(),
encoded: "Hello World!".to_string(),
}],
source_info: SourceInfo::new(1, 4, 1, 16),
},
}
);
}
#[test]
fn test_raw_string_base64_prefix() {
let mut reader = Reader::init("```base64_inline```");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: None,
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "base64_inline".to_string(),
encoded: "base64_inline".to_string(),
}],
source_info: SourceInfo::new(1, 4, 1, 17),
},
}
);
}
#[test]
fn test_raw_string_csv() {
let mut reader = Reader::init("```\nline1\nline2\nline3\n```");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(1, 4, 1, 4),
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(1, 4, 2, 1),
}
}),
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "line1\nline2\nline3\n".to_string(),
encoded: "line1\nline2\nline3\n".to_string(),
}],
source_info: SourceInfo::new(2, 1, 5, 1),
},
}
);
}
#[test]
fn test_raw_string_one_emptyline() {
// one newline
// the value takes the value of the newline??
let mut reader = Reader::init("```\n\n```");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(1, 4, 1, 4),
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(1, 4, 2, 1),
}
}),
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "\n".to_string(),
encoded: "\n".to_string(),
}],
source_info: SourceInfo::new(2, 1, 3, 1),
},
}
);
// one cr
let mut reader = Reader::init("```\n\r\n````");
assert_eq!(
multiline_string(&mut reader).unwrap(),
RawString {
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(1, 4, 1, 4),
},
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(1, 4, 2, 1),
}
}),
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "\r\n".to_string(),
encoded: "\r\n".to_string(),
}],
source_info: SourceInfo::new(2, 1, 3, 1),
},
}
);
}
#[test]
fn test_raw_string_error() {
let mut reader = Reader::init("xxx");
let error = multiline_string(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("```")
}
);
assert!(error.recoverable);
let mut reader = Reader::init("```\nxxx");
let error = multiline_string(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 2, column: 4 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("```")
}
);
assert!(!error.recoverable);
let mut reader = Reader::init("```xxx");
let error = multiline_string(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 7 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("```")
}
);
assert!(!error.recoverable);
}
#[test]
fn test_raw_string_value() {
let mut reader = Reader::init("```");
assert_eq!(
multiline_string_value(&mut reader).unwrap(),
Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(1, 1, 1, 1),
}
);
assert_eq!(reader.state.cursor, 3);
let mut reader = Reader::init("hello```");
assert_eq!(
multiline_string_value(&mut reader).unwrap(),
Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "hello".to_string(),
encoded: "hello".to_string(),
}],
source_info: SourceInfo::new(1, 1, 1, 6),
}
);
assert_eq!(reader.state.cursor, 8);
}
}

View File

@ -407,10 +407,17 @@ mod tests {
source_info: SourceInfo::new(2, 1, 2, 1),
},
value: Bytes::RawString(RawString {
newline: Whitespace {
value: "\n".to_string(),
source_info: SourceInfo::new(2, 4, 3, 1),
},
lang: Some(Lang {
value: None,
space: Whitespace {
value: "".to_string(),
source_info: SourceInfo::new(2, 4, 2, 4),
},
newline: Whitespace {
source_info: SourceInfo::new(2, 4, 3, 1),
value: "\n".to_string(),
}
}),
value: Template {
elements: vec![TemplateElement::String {
value: String::from("Hello World!\n"),

View File

@ -24,6 +24,7 @@ use super::primitives::*;
use super::reader::Reader;
use super::string::*;
use super::ParseResult;
use crate::parser::multiline::multiline_string;
use crate::parser::{Error, ParseError};
pub fn predicate_value(reader: &mut Reader) -> ParseResult<'static, PredicateValue> {
@ -61,7 +62,7 @@ pub fn predicate_value(reader: &mut Reader) -> ParseResult<'static, PredicateVal
Ok(value) => Ok(PredicateValue::String(value)),
Err(e) => Err(e),
},
|p1| match raw_string(p1) {
|p1| match multiline_string(p1) {
Ok(value) => Ok(PredicateValue::Raw(value)),
Err(e) => Err(e),
},

View File

@ -23,7 +23,6 @@ use super::error::*;
use super::filename;
use super::reader::Reader;
use super::string::*;
use super::template;
use super::ParseResult;
pub fn space(reader: &mut Reader) -> ParseResult<'static, Whitespace> {
@ -502,60 +501,6 @@ pub fn float(reader: &mut Reader) -> ParseResult<'static, Float> {
Ok(Float { value, encoded })
}
pub fn raw_string(reader: &mut Reader) -> ParseResult<'static, RawString> {
// one value without newline or multiline mode
// includes the last newline (consistent with bash EOL)
try_literal("```", reader)?;
let save = reader.state.clone();
match newline(reader) {
Ok(newline) => {
let value = raw_string_value(reader)?;
Ok(RawString { newline, value })
}
Err(_) => {
reader.state = save;
let newline = Whitespace {
value: String::from(""),
source_info: SourceInfo {
start: reader.state.clone().pos,
end: reader.state.clone().pos,
},
};
let value = raw_string_value(reader)?;
Ok(RawString { newline, value })
}
}
}
pub fn raw_string_value(reader: &mut Reader) -> ParseResult<'static, Template> {
let mut chars = vec![];
let start = reader.state.pos.clone();
while !reader.remaining().starts_with("```") && !reader.is_eof() {
let pos = reader.state.pos.clone();
let c = reader.read().unwrap();
chars.push((c, c.to_string(), pos));
}
let end = reader.state.pos.clone();
literal("```", reader)?;
let encoded_string = template::EncodedString {
source_info: SourceInfo {
start: start.clone(),
end: end.clone(),
},
chars,
};
let elements = template::templatize(encoded_string)?;
Ok(Template {
quotes: false,
elements,
source_info: SourceInfo { start, end },
})
}
pub(crate) fn file(reader: &mut Reader) -> ParseResult<'static, File> {
let _start = reader.state.clone();
try_literal("file", reader)?;
@ -1120,226 +1065,6 @@ mod tests {
assert!(!error.recoverable);
}
#[test]
fn test_raw_string_empty() {
let mut reader = Reader::init("``````");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from(""),
source_info: SourceInfo::new(1, 4, 1, 4),
},
value: Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(1, 4, 1, 4),
},
}
);
let mut reader = Reader::init("```\n```");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from("\n"),
source_info: SourceInfo::new(1, 4, 2, 1),
},
value: Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(2, 1, 2, 1),
},
}
);
let mut reader = Reader::init("```\r\n```");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from("\r\n"),
source_info: SourceInfo::new(1, 4, 2, 1),
},
value: Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(2, 1, 2, 1),
},
}
);
}
#[test]
fn test_raw_string_hello() {
let mut reader = Reader::init("```Hello World!```");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from(""),
source_info: SourceInfo::new(1, 4, 1, 4),
},
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "Hello World!".to_string(),
encoded: "Hello World!".to_string(),
}],
source_info: SourceInfo::new(1, 4, 1, 16),
},
}
);
let mut reader = Reader::init("```Hello\nWorld!\n```");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from(""),
source_info: SourceInfo::new(1, 4, 1, 4),
},
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "Hello\nWorld!\n".to_string(),
encoded: "Hello\nWorld!\n".to_string(),
}],
source_info: SourceInfo::new(1, 4, 3, 1),
},
}
);
}
#[test]
fn test_raw_string_csv() {
let mut reader = Reader::init("```\nline1\nline2\nline3\n```");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from("\n"),
source_info: SourceInfo::new(1, 4, 2, 1),
},
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "line1\nline2\nline3\n".to_string(),
encoded: "line1\nline2\nline3\n".to_string(),
}],
source_info: SourceInfo::new(2, 1, 5, 1),
},
}
);
}
#[test]
fn test_raw_string_one_emptyline() {
// one newline
// the value takes the value of the newline??
let mut reader = Reader::init("```\n\n```");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from("\n"),
source_info: SourceInfo::new(1, 4, 2, 1),
},
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "\n".to_string(),
encoded: "\n".to_string(),
}],
source_info: SourceInfo::new(2, 1, 3, 1),
},
}
);
// one cr
let mut reader = Reader::init("```\n\r\n````");
assert_eq!(
raw_string(&mut reader).unwrap(),
RawString {
newline: Whitespace {
value: String::from("\n"),
source_info: SourceInfo::new(1, 4, 2, 1),
},
value: Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "\r\n".to_string(),
encoded: "\r\n".to_string(),
}],
source_info: SourceInfo::new(2, 1, 3, 1),
},
}
);
}
#[test]
fn test_raw_string_error() {
let mut reader = Reader::init("xxx");
let error = raw_string(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("```")
}
);
assert!(error.recoverable);
let mut reader = Reader::init("```\nxxx");
let error = raw_string(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 2, column: 4 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("```")
}
);
assert!(!error.recoverable);
let mut reader = Reader::init("```xxx");
let error = raw_string(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 7 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("```")
}
);
assert!(!error.recoverable);
}
#[test]
fn test_raw_string_value() {
let mut reader = Reader::init("```");
assert_eq!(
raw_string_value(&mut reader).unwrap(),
Template {
quotes: false,
elements: vec![],
source_info: SourceInfo::new(1, 1, 1, 1),
}
);
assert_eq!(reader.state.cursor, 3);
let mut reader = Reader::init("hello```");
assert_eq!(
raw_string_value(&mut reader).unwrap(),
Template {
quotes: false,
elements: vec![TemplateElement::String {
value: "hello".to_string(),
encoded: "hello".to_string(),
}],
source_info: SourceInfo::new(1, 1, 1, 6),
}
);
assert_eq!(reader.state.cursor, 8);
}
#[test]
fn test_hex_digit() {
let mut reader = Reader::init("0");

View File

@ -141,10 +141,20 @@ impl ToJson for Bytes {
("type".to_string(), JValue::String("xml".to_string())),
("value".to_string(), JValue::String(value.clone())),
]),
Bytes::RawString(value) => JValue::Object(vec![
("type".to_string(), JValue::String("raw-string".to_string())),
("value".to_string(), JValue::String(value.value.to_string())),
]),
Bytes::RawString(value) => {
let lang = match &value.lang {
// TODO: change raw-string for undefined
None | Some(Lang { value: None, .. }) => "raw-string".to_string(),
Some(Lang {
value: Some(lang_value),
..
}) => lang_value.to_string(),
};
JValue::Object(vec![
("type".to_string(), JValue::String(lang)),
("value".to_string(), JValue::String(value.value.to_string())),
])
}
}
}
}

View File

@ -121,5 +121,6 @@ pub fn format_token(token: Token, color: bool) -> String {
value
}
}
Token::Lang(value) => value,
}
}

View File

@ -41,6 +41,7 @@ pub enum Token {
String(String),
CodeDelimiter(String),
CodeVariable(String),
Lang(String),
}
pub trait Tokenizable {
@ -605,16 +606,35 @@ impl Tokenizable for PredicateValue {
impl Tokenizable for RawString {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![
Token::Keyword("```".to_string()),
Token::Whitespace(self.newline.value.clone()),
];
let mut tokens: Vec<Token> = vec![Token::Keyword("```".to_string())];
if let Some(kind) = &self.lang {
tokens.append(&mut kind.tokenize())
}
tokens.append(&mut self.value.tokenize());
tokens.push(Token::Keyword("```".to_string()));
tokens
}
}
impl Tokenizable for Lang {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
if let Some(value) = &self.value {
tokens.append(&mut value.tokenize());
}
tokens.append(&mut self.space.tokenize());
tokens.append(&mut self.newline.tokenize());
tokens
}
}
impl Tokenizable for LangValue {
fn tokenize(&self) -> Vec<Token> {
let token = Token::Lang(self.to_string());
vec![token]
}
}
impl Tokenizable for EncodedString {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];

View File

@ -520,7 +520,7 @@ impl Lintable<RawString> for RawString {
fn lint(&self) -> RawString {
RawString {
newline: self.newline.clone(),
lang: None,
value: self.value.lint(),
}
}