diff --git a/packages/hurl_core/src/format/html.rs b/packages/hurl_core/src/format/html.rs
index ed013a490..6b273d580 100644
--- a/packages/hurl_core/src/format/html.rs
+++ b/packages/hurl_core/src/format/html.rs
@@ -53,12 +53,12 @@ pub fn format(hurl_file: HurlFile, standalone: bool) -> String {
impl Htmlable for HurlFile {
fn to_html(&self) -> String {
let mut buffer = String::from("");
- buffer.push_str("
");
+ buffer.push_str("
");
for entry in self.clone().entries {
buffer.push_str(entry.to_html().as_str());
}
add_line_terminators(&mut buffer, self.line_terminators.clone());
- buffer.push_str("
");
+ buffer.push_str("");
buffer
}
}
@@ -66,12 +66,12 @@ impl Htmlable for HurlFile {
impl Htmlable for Entry {
fn to_html(&self) -> String {
let mut buffer = String::from("");
- buffer.push_str("");
+ buffer.push_str("");
buffer.push_str(self.request.to_html().as_str());
if let Some(response) = self.clone().response {
buffer.push_str(response.to_html().as_str());
}
- buffer.push_str("
");
+ buffer.push_str("");
buffer
}
}
@@ -79,16 +79,17 @@ impl Htmlable for Entry {
impl Htmlable for Request {
fn to_html(&self) -> String {
let mut buffer = String::from("");
- buffer.push_str("");
+ buffer.push_str("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
+
buffer.push_str("");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.method.to_html().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(format!("{}", self.url.to_html()).as_str());
- buffer.push_str(self.line_terminator0.to_html().as_str());
buffer.push_str("");
- buffer.push_str("
");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
+
for header in self.headers.clone() {
buffer.push_str(header.to_html().as_str());
}
@@ -98,6 +99,7 @@ impl Htmlable for Request {
if let Some(body) = self.body.clone() {
buffer.push_str(body.to_html().as_str());
}
+ buffer.push_str("");
buffer
}
}
@@ -105,7 +107,7 @@ impl Htmlable for Request {
impl Htmlable for Response {
fn to_html(&self) -> String {
let mut buffer = String::from("");
- buffer.push_str("");
+ buffer.push_str("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("");
buffer.push_str(self.space0.to_html().as_str());
@@ -113,6 +115,7 @@ impl Htmlable for Response {
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(self.status.to_html().as_str());
buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
for header in self.headers.clone() {
buffer.push_str(header.to_html().as_str());
}
@@ -122,7 +125,7 @@ impl Htmlable for Response {
if let Some(body) = self.body.clone() {
buffer.push_str(body.to_html().as_str());
}
- buffer.push_str("
");
+ buffer.push_str("");
buffer
}
}
@@ -144,7 +147,7 @@ impl Htmlable for Version {
impl Htmlable for Status {
fn to_html(&self) -> String {
- format!("{}", self.value.to_string())
+ format!("{}", self.value.to_string())
}
}
@@ -160,6 +163,7 @@ impl Htmlable for Section {
)
.as_str(),
);
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer.push_str(self.value.to_html().as_str());
buffer
}
@@ -215,8 +219,8 @@ impl Htmlable for KeyValue {
buffer.push_str(":");
buffer.push_str(self.space2.to_html().as_str());
buffer.push_str(format!("{}", self.value.to_html()).as_str());
- buffer.push_str(self.line_terminator0.to_html().as_str());
buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
@@ -241,8 +245,8 @@ impl Htmlable for FileParam {
buffer.push(':');
buffer.push_str(self.space2.to_html().as_str());
buffer.push_str(self.value.to_html().as_str());
- buffer.push_str(self.line_terminator0.to_html().as_str());
buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
@@ -267,8 +271,9 @@ impl Htmlable for FileValue {
impl Htmlable for Filename {
fn to_html(&self) -> String {
- let mut buffer = String::from("");
+ let mut buffer = String::from("");
buffer.push_str(self.value.as_str());
+ buffer.push_str("");
buffer
}
}
@@ -279,21 +284,24 @@ impl Htmlable for Cookie {
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("");
buffer.push_str(self.space0.to_html().as_str());
- buffer.push_str(self.name.value.as_str());
+ buffer
+ .push_str(format!("{}", self.name.value.as_str()).as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(":");
buffer.push_str(self.space2.to_html().as_str());
buffer.push_str(self.value.to_html().as_str());
buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
impl Htmlable for CookieValue {
fn to_html(&self) -> String {
- let mut buffer = String::from("");
- buffer.push_str(self.value.as_str());
- buffer
+ format!(
+ "{}",
+ self.value.as_str()
+ )
}
}
@@ -303,12 +311,14 @@ impl Htmlable for Capture {
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("");
buffer.push_str(self.space0.to_html().as_str());
- buffer.push_str(self.name.value.as_str());
+ buffer
+ .push_str(format!("{}", self.name.value.as_str()).as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(":");
buffer.push_str(self.space2.to_html().as_str());
buffer.push_str(self.query.to_html().as_str());
buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
@@ -346,7 +356,7 @@ impl Htmlable for QueryValue {
);
}
QueryValue::Body {} => {
- buffer.push_str("status");
+ buffer.push_str("body");
}
QueryValue::Xpath { space0, expr } => {
buffer.push_str("xpath");
@@ -398,13 +408,15 @@ impl Htmlable for Subquery {
let mut buffer = String::from("");
match self.value.clone() {
SubqueryValue::Regex { expr, space0 } => {
- buffer.push_str("regex");
+ buffer.push_str("regex");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("\"{}\"", expr.to_html()).as_str(),
);
}
- SubqueryValue::Count {} => buffer.push_str("count"),
+ SubqueryValue::Count {} => {
+ buffer.push_str("count")
+ }
}
buffer
}
@@ -443,6 +455,7 @@ impl Htmlable for Assert {
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(self.predicate.to_html().as_str());
buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
@@ -451,7 +464,7 @@ impl Htmlable for Predicate {
fn to_html(&self) -> String {
let mut buffer = String::from("");
if self.not {
- buffer.push_str("not");
+ buffer.push_str("not");
buffer.push_str(self.space0.to_html().as_str());
}
buffer.push_str(self.predicate_func.to_html().as_str());
@@ -617,9 +630,7 @@ impl Htmlable for PredicateValue {
format!("{}", value.to_string())
}
PredicateValue::Bool(value) => format!("{}", value),
- PredicateValue::Hex(value) => {
- format!("{}", value.to_string())
- }
+ PredicateValue::Hex(value) => value.to_html(),
PredicateValue::Base64(value) => value.to_html(),
PredicateValue::Expression(value) => value.to_html(),
PredicateValue::Null {} => "null".to_string(),
@@ -629,28 +640,43 @@ impl Htmlable for PredicateValue {
impl Htmlable for RawString {
fn to_html(&self) -> String {
- let mut buffer = String::from("```");
- if !self.newline.to_html().as_str().is_empty() {
- buffer.push_str("");
+ let mut buffer = "".to_string();
+ buffer.push_str("");
+ buffer.push_str("```");
+
+ if !self.newline.value.as_str().is_empty() {
+ buffer.push_str(
+ format!(
+ "{}",
+ self.newline.value.as_str()
+ )
+ .as_str(),
+ );
}
- let end_newline = self.value.to_string().ends_with('\n');
let mut lines: Vec = regex::Regex::new(r"\n|\r\n")
.unwrap()
.split(self.value.to_string().trim())
- .map(|l| l.to_string())
+ .map(|l| xml_escape(l.to_string()))
+ .filter(|l| !l.is_empty())
.collect();
- buffer.push_str(xml_escape(lines.remove(0)).as_str());
-
- for line in lines {
- buffer.push_str("");
- buffer.push_str(xml_escape(line).as_str());
+ if lines.is_empty() {
+ buffer.push_str("```");
+ } else if lines.len() == 1 {
+ buffer.push_str(encode_html(lines.get(0).unwrap().to_string()).as_str());
+ buffer.push_str("```");
+ } else {
+ buffer.push_str(encode_html(lines.remove(0)).as_str());
+ buffer.push_str("\n");
+ for line in lines {
+ buffer.push_str("");
+ buffer.push_str(encode_html(line).as_str());
+ buffer.push_str("\n");
+ }
+ buffer.push_str("```");
}
- if end_newline {
- buffer.push_str("");
- }
- buffer.push_str("```");
+ buffer.push_str("");
buffer
}
}
@@ -659,70 +685,46 @@ impl Htmlable for Body {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
- buffer.push_str("");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.value.to_html().as_str());
- buffer.push_str("");
+ buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
impl Htmlable for Bytes {
fn to_html(&self) -> String {
- let mut buffer = String::from("");
match self {
- Bytes::Base64(value) => {
- buffer.push_str(value.to_html().as_str());
- }
- Bytes::Hex(value) => {
- buffer.push_str(value.to_html().as_str());
- }
- Bytes::File(value) => {
- buffer.push_str(value.to_html().as_str());
- }
- Bytes::RawString(value) => {
- buffer.push_str(value.to_html().as_str());
- }
- Bytes::Json { value } => buffer.push_str(value.to_html().as_str()),
- Bytes::Xml { value } => {
- let mut lines: Vec = regex::Regex::new(r"\n|\r\n")
- .unwrap()
- .split(value.as_str())
- .map(|l| l.to_string())
- .collect();
- buffer.push_str(xml_escape(lines.remove(0)).as_str());
- for line in lines {
- buffer.push_str("");
- buffer.push_str(xml_escape(line).as_str());
- buffer.push_str("");
- }
- }
+ Bytes::Base64(value) => format!("{}", value.to_html()),
+ Bytes::File(value) => format!("{}", value.to_html()),
+ Bytes::Hex(value) => format!("{}", value.to_html()),
+ Bytes::Json { value } => value.to_html(),
+ Bytes::RawString(value) => value.to_html(),
+ Bytes::Xml { value } => xml_html(value),
}
- buffer
}
}
-fn xml_escape(s: String) -> String {
- s.replace('<', "<")
- .replace('>', ">")
- .replace('&', "&")
+// you should probably define for XML value to be consistent with the other types
+fn xml_html(value: &str) -> String {
+ let mut buffer = String::from("");
+ buffer.push_str(multilines(value.to_string()).as_str());
+ buffer.push_str("");
+ buffer
}
+fn xml_escape(s: String) -> String {
+ s.replace('&', "&")
+ .replace('<', "<")
+ .replace('>', ">")
+}
+
+// Improvement: break into spans within the json value
impl Htmlable for JsonValue {
fn to_html(&self) -> String {
- let s = self.to_string();
- let mut lines: Vec = regex::Regex::new(r"\n|\r\n")
- .unwrap()
- .split(s.as_str())
- .map(|l| l.to_string())
- .collect();
- let mut buffer = String::from("");
- buffer.push_str(lines.remove(0).as_str());
- for line in lines {
- buffer.push_str("");
- buffer.push_str(line.as_str());
- buffer.push_str("");
- }
+ let mut buffer = String::from("");
+ buffer.push_str(multilines(self.encoded()).as_str());
+ buffer.push_str("");
buffer
}
}
@@ -743,48 +745,52 @@ impl Htmlable for LineTerminator {
let mut buffer = String::from("");
buffer.push_str(self.space0.to_html().as_str());
if let Some(v) = self.clone().comment {
- buffer.push_str("");
+ buffer.push_str(v.to_html().as_str());
}
+ buffer.push_str(self.newline.value.as_str());
+ buffer
+ }
+}
+
+impl Htmlable for Comment {
+ fn to_html(&self) -> String {
+ let mut buffer = String::from("");
buffer
}
}
impl Htmlable for File {
fn to_html(&self) -> String {
- let mut buffer = String::from("");
- buffer.push_str("file,");
+ let mut buffer = String::from("file,");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.filename.to_html().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push(';');
- buffer.push_str("");
buffer
}
}
impl Htmlable for Base64 {
fn to_html(&self) -> String {
- let mut buffer = String::from("");
- buffer.push_str("base64,");
+ let mut buffer = String::from("base64,");
buffer.push_str(self.space0.to_html().as_str());
- buffer.push_str(self.encoded.as_str());
+ buffer
+ .push_str(format!("{}", self.encoded.as_str()).as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push(';');
- buffer.push_str("");
buffer
}
}
+
impl Htmlable for Hex {
fn to_html(&self) -> String {
- let mut buffer = String::from("");
- buffer.push_str("hex,");
+ let mut buffer = String::from("hex,");
buffer.push_str(self.space0.to_html().as_str());
- buffer.push_str(self.encoded.as_str());
+ buffer.push_str(format!("{}", self.encoded.as_str()).as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push(';');
- buffer.push_str("");
buffer
}
}
@@ -797,7 +803,15 @@ impl Htmlable for EncodedString {
impl Htmlable for Template {
fn to_html(&self) -> String {
- xml_escape(self.to_string().replace("\n", "\\n"))
+ let mut s = "".to_string();
+ for element in self.elements.clone() {
+ let elem_str = match element {
+ TemplateElement::String { encoded, .. } => encoded,
+ TemplateElement::Expression(expr) => format!("{{{{{}}}}}", expr.to_string()),
+ };
+ s.push_str(elem_str.as_str())
+ }
+ xml_escape(s)
}
}
@@ -814,11 +828,11 @@ impl Htmlable for Expr {
fn add_line_terminators(buffer: &mut String, line_terminators: Vec) {
for line_terminator in line_terminators.clone() {
buffer.push_str("");
- buffer.push_str(line_terminator.to_html().as_str());
if line_terminator.newline.value.is_empty() {
buffer.push_str("
");
}
buffer.push_str("");
+ buffer.push_str(line_terminator.to_html().as_str());
}
}
@@ -826,6 +840,15 @@ fn encode_html(s: String) -> String {
s.replace(">", ">").replace("<", "<")
}
+fn multilines(s: String) -> String {
+ regex::Regex::new(r"\n|\r\n")
+ .unwrap()
+ .split(s.as_str())
+ .map(|l| format!("{}", xml_escape(l.to_string())))
+ .collect::>()
+ .join("\n")
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -847,7 +870,10 @@ mod tests {
source_info: SourceInfo::init(0, 0, 0, 0),
},
};
- assert_eq!(raw_string.to_html(), "``````".to_string());
+ assert_eq!(
+ raw_string.to_html(),
+ "``````".to_string()
+ );
// ```hello```
let raw_string = RawString {
@@ -864,7 +890,10 @@ mod tests {
source_info: SourceInfo::init(0, 0, 0, 0),
},
};
- assert_eq!(raw_string.to_html(), "```hello```".to_string());
+ assert_eq!(
+ raw_string.to_html(),
+ "```hello```".to_string()
+ );
// ```
// line1
@@ -884,6 +913,81 @@ mod tests {
source_info: SourceInfo::init(0, 0, 0, 0),
},
};
- assert_eq!(raw_string.to_html(), "```line1line2```".to_string());
+ assert_eq!(
+ raw_string.to_html(),
+ "```\nline1\nline2\n```".to_string()
+ );
+ }
+
+ #[test]
+ fn test_multilines() {
+ assert_eq!(
+ multilines("{\n \"id\": 1\n}".to_string()),
+ "{\n \"id\": 1\n}"
+ );
+ assert_eq!(
+ multilines("\ncafé".to_string()),
+ "<?xml version=\"1.0\"?>\n<drink>café</drink>"
+ );
+ }
+
+ #[test]
+ fn test_json() {
+ let value = JsonValue::Object {
+ space0: "".to_string(),
+ elements: vec![JsonObjectElement {
+ space0: "\n ".to_string(),
+ name: Template {
+ quotes: true,
+ elements: vec![TemplateElement::String {
+ value: "id".to_string(),
+ encoded: "id".to_string(),
+ }],
+ source_info: SourceInfo::init(0, 0, 0, 0),
+ },
+ space1: "".to_string(),
+ space2: " ".to_string(),
+ value: JsonValue::Number("1".to_string()),
+ space3: "\n".to_string(),
+ }],
+ };
+ assert_eq!(
+ value.to_html(),
+ "{\n \"id\": 1\n}"
+ );
+ }
+
+ #[test]
+ fn test_json_encoded_newline() {
+ let value = JsonValue::String(Template {
+ quotes: true,
+ elements: vec![TemplateElement::String {
+ value: "\n".to_string(),
+ encoded: "\\n".to_string(),
+ }],
+ source_info: SourceInfo::init(0, 0, 0, 0),
+ });
+ assert_eq!(
+ value.to_html(),
+ "\"\\n\""
+ )
+ }
+
+ #[test]
+ fn test_xml() {
+ let value = "\ncafé";
+ assert_eq!(
+ xml_html(value),
+ "<?xml version=\"1.0\"?>\n<drink>café</drink>"
+ )
+ }
+
+ #[test]
+ fn test_xml_escape() {
+ assert_eq!(xml_escape("hello".to_string()), "hello");
+ assert_eq!(
+ xml_escape("".to_string()),
+ "<?xml version=\"1.0\"?>"
+ );
}
}
diff --git a/packages/hurl_core/src/format/hurl.css b/packages/hurl_core/src/format/hurl.css
index 10511c324..ee73f190c 100644
--- a/packages/hurl_core/src/format/hurl.css
+++ b/packages/hurl_core/src/format/hurl.css
@@ -4,7 +4,6 @@ body {
}
span.line {
- display: block;
line-height: 1.2rem;
}
@@ -31,7 +30,7 @@ span.line:before {
.version {
color: black;
}
-.status {
+.number {
color: blue;
}
.section-header {
@@ -42,6 +41,14 @@ span.line:before {
color: teal;
}
+.subquery-type {
+ color: darkblue;
+}
+
+.not {
+ color: darkblue;
+}
+
.predicate-type {
color: darkblue;
}
@@ -49,7 +56,30 @@ span.line:before {
.string {
color: darkgreen;
}
-
+.raw {
+ color: darkgreen;
+}
.comment {
color: dimgray;
}
+.name {
+ color: darkgreen;
+}
+.json {
+ color: darkgreen;
+}
+.xml {
+ color: darkgreen;
+}
+.base64 {
+ color: darkgreen;
+}
+.hex {
+ color: darkgreen;
+}
+.filename {
+ color: darkgreen;
+}
+.cookie-value {
+ color: darkgreen;
+}
\ No newline at end of file