Reorganize code between hurl and hurlfmt

This commit is contained in:
Fabrice Reix 2020-11-02 21:25:19 +01:00
parent 946482e45d
commit d18358b2b7
30 changed files with 140 additions and 1992 deletions

View File

@ -20,7 +20,7 @@ jobs:
os: linux
dist: bionic
before_script:
- export VERSION=$(grep '^version' Cargo.toml | cut -f2 -d'"')
- export VERSION=$(grep '^version' packages/hurl/Cargo.toml | cut -f2 -d'"')
- sudo apt install -y libcurl4-openssl-dev
script:
- ci/man.sh
@ -39,7 +39,7 @@ jobs:
os: osx
before_script:
- brew install jq
- export VERSION=$(grep '^version' Cargo.toml | cut -f2 -d'"')
- export VERSION=$(grep '^version' packages/hurl/Cargo.toml | cut -f2 -d'"')
script:
- ci/man.sh
- ci/release.sh

View File

@ -15,6 +15,8 @@
* limitations under the License.
*
*/
#[allow(unused)]
pub enum TerminalColor {
Black,
Red,

View File

@ -16,9 +16,11 @@
*
*/
use super::error::Error;
use crate::format::TerminalColor;
use crate::runner;
use super::color::TerminalColor;
use hurl_core::error::Error;
use hurl_core::parser;
pub fn make_logger_verbose(verbose: bool) -> impl Fn(&str) {

View File

@ -16,14 +16,10 @@
*
*/
pub use self::error::Error;
pub use self::logger::{
log_info, make_logger_error_message, make_logger_parser_error, make_logger_runner_error,
make_logger_verbose,
};
pub use self::options::cookies_output_file;
pub use self::options::CLIError;
mod error;
mod color;
mod logger;
mod options;

View File

@ -1,32 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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.
*
*/
pub struct CLIError {
pub message: String,
}
pub fn cookies_output_file(filename: String, n: usize) -> Result<std::path::PathBuf, CLIError> {
if n > 1 {
Err(CLIError {
message: "Only save cookies for a unique session".to_string(),
})
} else {
let path = std::path::Path::new(&filename);
Ok(path.to_path_buf())
}
}

View File

@ -1,571 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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 hurl_core::ast::*;
pub trait Htmlable {
fn to_html(&self) -> String;
}
pub fn format_standalone(hurl_file: HurlFile) -> String {
let css = include_str!("hurl.css");
let mut buffer = String::from("");
buffer.push_str("<!DOCTYPE html>\n");
buffer.push_str("<html>");
buffer.push_str("<head>");
buffer.push_str("<title>Hurl File</title>");
buffer.push_str("<style>\n");
buffer.push_str(css);
buffer.push_str("</style>");
buffer.push_str("</head>");
buffer.push_str("<body>\n");
buffer.push_str(hurl_file.to_html().as_str());
buffer.push_str("\n</body>");
buffer.push_str("</html>");
buffer
}
pub fn format(hurl_file: HurlFile, standalone: bool) -> String {
if standalone {
format_standalone(hurl_file)
} else {
hurl_file.to_html()
}
}
impl Htmlable for HurlFile {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str("<div class=\"hurl-file\">");
for entry in self.clone().entries {
buffer.push_str(entry.to_html().as_str());
}
for line_terminator in self.line_terminators.clone() {
buffer.push_str(line_terminator.to_html().as_str());
}
buffer.push_str("</div>");
buffer
}
}
impl Htmlable for Entry {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str("<div class=\"hurl-entry\">");
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("</div>");
buffer
}
}
impl Htmlable for Request {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str("<div class=\"request\">");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
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(self.url.to_html().as_str());
buffer.push_str(self.line_terminator0.to_html().as_str());
buffer.push_str("</span>");
buffer.push_str("</div>");
for header in self.headers.clone() {
buffer.push_str(header.to_html().as_str());
}
for section in self.sections.clone() {
buffer.push_str(section.to_html().as_str());
}
buffer
}
}
impl Htmlable for Response {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str("<div class=\"response\">");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.version.to_html().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(self.status.to_html().as_str());
buffer.push_str("</span>");
for section in self.sections.clone() {
buffer.push_str(section.to_html().as_str());
}
buffer.push_str("</div>");
buffer
}
}
impl Htmlable for Method {
fn to_html(&self) -> String {
return format!("<span class=\"method\">{}</span>", self.as_str());
}
}
impl Htmlable for Version {
fn to_html(&self) -> String {
return format!(
"<span class=\"version\">HTTP/{}</span>",
self.value.as_str()
);
}
}
impl Htmlable for Status {
fn to_html(&self) -> String {
format!("<span class=\"status\">{}</span>", self.value.to_string())
}
}
impl Htmlable for Section {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str(self.space0.to_html().as_str());
buffer
.push_str(format!("<span class=\"section-header\">[{}]</span>", self.name()).as_str());
buffer.push_str("</span>");
buffer.push_str(self.value.to_html().as_str());
buffer
}
}
impl Htmlable for SectionValue {
fn to_html(&self) -> String {
let mut buffer = String::from("");
match self {
SectionValue::Asserts(items) => {
for item in items {
buffer.push_str(item.to_html().as_str())
}
}
SectionValue::QueryParams(items) => {
for item in items {
buffer.push_str(item.to_html().as_str())
}
}
SectionValue::FormParams(items) => {
for item in items {
buffer.push_str(item.to_html().as_str())
}
}
SectionValue::MultipartFormData(items) => {
for item in items {
buffer.push_str(item.to_html().as_str())
}
}
SectionValue::Cookies(items) => {
for item in items {
buffer.push_str(item.to_html().as_str())
}
}
SectionValue::Captures(items) => {
for item in items {
buffer.push_str(item.to_html().as_str())
}
}
}
buffer
}
}
impl Htmlable for KeyValue {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.key.to_html().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str("<span>:</span>");
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("</span>");
buffer
}
}
impl Htmlable for MultipartParam {
fn to_html(&self) -> String {
match self {
MultipartParam::Param(keyvalue) => keyvalue.to_html(),
MultipartParam::FileParam(file_param) => file_param.to_html(),
}
}
}
impl Htmlable for FileParam {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.key.to_html().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str("<span>:</span>");
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("</span>");
buffer
}
}
impl Htmlable for FileValue {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str(self.space0.to_html().as_str());
buffer
}
}
impl Htmlable for Cookie {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.name.value.as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str("<span>:</span>");
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("</span>");
buffer
}
}
impl Htmlable for CookieValue {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str(self.value.as_str());
buffer
}
}
impl Htmlable for Capture {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.name.value.as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str("<span>:</span>");
buffer.push_str(self.space2.to_html().as_str());
buffer.push_str(self.query.to_html().as_str());
buffer.push_str(self.line_terminator0.to_html().as_str());
buffer.push_str("</span>");
buffer
}
}
impl Htmlable for Query {
fn to_html(&self) -> String {
self.value.to_html()
}
}
impl Htmlable for QueryValue {
fn to_html(&self) -> String {
let mut buffer = String::from("");
match self {
QueryValue::Status {} => {
buffer.push_str("<span class=\"query-type\">status</span>");
}
QueryValue::Header { space0, name } => {
buffer.push_str("<span class=\"query-type\">header</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(name.to_html().as_str());
}
QueryValue::Cookie { space0, expr } => {
buffer.push_str("<span class=\"query-type\">cookie</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(expr.to_html().as_str());
}
QueryValue::Body {} => {
buffer.push_str("<span class=\"query-type\">status</span>");
}
QueryValue::Xpath { space0, expr } => {
buffer.push_str("<span class=\"query-type\">xpath</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(expr.to_html().as_str());
}
QueryValue::Jsonpath { space0, expr } => {
buffer.push_str("<span class=\"query-type\">jsonpath</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(expr.to_html().as_str());
}
QueryValue::Regex { space0, expr } => {
buffer.push_str("<span class=\"query-type\">regex</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(expr.to_html().as_str());
}
QueryValue::Variable { space0, name } => {
buffer.push_str("<span class=\"query-type\">variable</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(name.to_html().as_str());
}
}
buffer
}
}
impl Htmlable for CookiePath {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str(self.name.to_html().as_str());
if let Some(attribute) = self.attribute.clone() {
buffer.push_str(attribute.to_html().as_str());
}
buffer
}
}
impl Htmlable for CookieAttribute {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.name.value().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer
}
}
impl Htmlable for Assert {
fn to_html(&self) -> String {
let mut buffer = String::from("");
add_line_terminators(&mut buffer, self.line_terminators.clone());
buffer.push_str("<span class=\"line\">");
buffer.push_str(self.space0.to_html().as_str());
buffer.push_str(self.query.to_html().as_str());
buffer.push_str(self.space1.to_html().as_str());
buffer.push_str(self.predicate.to_html().as_str());
buffer.push_str("</span>");
buffer.push_str(self.line_terminator0.to_html().as_str());
buffer
}
}
impl Htmlable for Predicate {
fn to_html(&self) -> String {
let mut buffer = String::from("");
if self.not {
buffer.push_str("not");
buffer.push_str(self.space0.to_html().as_str());
}
buffer.push_str(self.predicate_func.to_html().as_str());
buffer
}
}
impl Htmlable for PredicateFunc {
fn to_html(&self) -> String {
self.value.to_html()
}
}
impl Htmlable for PredicateFuncValue {
fn to_html(&self) -> String {
let mut buffer = String::from("");
match self {
PredicateFuncValue::CountEqual { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(format!("<span class=\"number\">{}</span>", value).as_str());
}
PredicateFuncValue::EqualString { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"number\">{}</span>", value.to_html()).as_str(),
);
}
PredicateFuncValue::EqualInt { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(format!("<span class=\"number\">{}</span>", value).as_str());
}
PredicateFuncValue::EqualFloat { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"number\">{}</span>", value.to_string()).as_str(),
);
}
PredicateFuncValue::EqualExpression { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(value.to_html().as_str());
}
PredicateFuncValue::StartWith { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">startsWith</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
);
}
PredicateFuncValue::Contain { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">contains</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
);
}
PredicateFuncValue::IncludeString { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">includes</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
);
}
PredicateFuncValue::IncludeInt { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">includes</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(format!("<span class=\"number\">{}</span>", value).as_str());
}
PredicateFuncValue::IncludeNull { space0 } => {
buffer.push_str("<span class=\"predicate-type\">includes</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str("<span class=\"null\">null</span>");
}
PredicateFuncValue::IncludeBool { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">includes</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(format!("<span class=\"boolean\">{}</span>", value).as_str());
}
PredicateFuncValue::IncludeFloat { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">includes</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"number\">{}</span>", value.to_string()).as_str(),
);
}
PredicateFuncValue::IncludeExpression { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">includes</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(value.to_html().as_str());
}
PredicateFuncValue::Match { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">matches</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
);
}
PredicateFuncValue::EqualNull { space0 } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str("<span class=\"null\">null</span>");
}
PredicateFuncValue::EqualBool { space0, value } => {
buffer.push_str("<span class=\"predicate-type\">equals</span>");
buffer.push_str(space0.to_html().as_str());
buffer.push_str(format!("<span class=\"boolean\">{}</span>", value).as_str());
}
PredicateFuncValue::Exist {} => {
buffer.push_str("<span class=\"predicate-type\">exists</span>");
}
}
buffer
}
}
impl Htmlable for Whitespace {
fn to_html(&self) -> String {
let mut buffer = String::from("");
let Whitespace { value, .. } = self;
if !value.is_empty() {
buffer.push_str(self.value.as_str());
};
buffer
}
}
impl Htmlable for LineTerminator {
fn to_html(&self) -> String {
let mut buffer = String::from("");
buffer.push_str(self.space0.to_html().as_str());
if let Some(v) = self.clone().comment {
buffer.push_str("<span class=\"comment\">");
buffer.push_str(format!("#{}", v.value.as_str()).as_str());
buffer.push_str("</span>");
}
buffer
}
}
impl Htmlable for EncodedString {
fn to_html(&self) -> String {
format!("<span class=\"string\">\"{}\"</span>", self.encoded)
}
}
impl Htmlable for Template {
fn to_html(&self) -> String {
let mut buffer = String::from("");
for element in self.elements.clone() {
buffer.push_str(element.to_html().as_str());
}
buffer
}
}
impl Htmlable for TemplateElement {
fn to_html(&self) -> String {
match self {
TemplateElement::String { encoded, .. } => {
format!("<span class=\"string\">{}</span>", encoded)
}
TemplateElement::Expression(value) => value.to_html(),
/* space0: _, variable: _, space1: _ } => {
let mut buffer = String::from("");
buffer.push_str("{{");
buffer.push_str("}}");
return buffer;
}*/
}
}
}
impl Htmlable for Expr {
fn to_html(&self) -> String {
format!("<span class=\"variable\">{}</span>", self.variable.name)
}
}
fn to_line(v: String) -> String {
format!("<span class=\"line\">{}</span>", v)
}
fn add_line_terminators(buffer: &mut String, line_terminators: Vec<LineTerminator>) {
for line_terminator in line_terminators.clone() {
buffer.push_str(to_line(line_terminator.to_html()).as_str());
}
}

View File

@ -1,51 +0,0 @@
dy {
counter-reset: line;
font-family: monospace;
}
span.line {
display: block;
line-height: 1.2rem;
}
span.line:before {
counter-increment: line;
content: counter(line);
display: inline-block;
border-right: 1px solid #ddd;
padding: 0 1em;
margin-right: .5em;
color: #888;
width: 2.5em;
text-align: right;
}
.method {
color: black;
}
.version {
color: black;
}
.section-header {
color: darkmagenta;
}
.query-type {
color: teal;
}
.predicate-type {
color: darkblue;
}
.string {
color: darkgreen;
}
.comment {
color: dimgray;
}

View File

@ -1,27 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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.
*
*/
pub use self::color::TerminalColor;
pub use self::html::format as format_html;
pub use self::text::format as format_text;
pub use self::token::{Token, Tokenizable};
mod color;
mod html;
mod text;
mod token;

View File

@ -1,117 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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 hurl_core::ast::*;
use super::color::TerminalColor;
use super::token::*;
pub fn format(hurl_file: HurlFile, color: bool) -> String {
let mut buffer = String::from("");
for token in hurl_file.tokenize() {
buffer.push_str(format_token(token, color).as_str());
}
buffer
}
fn format_token(token: Token, color: bool) -> String {
match token {
Token::Whitespace(value) => value,
Token::Method(value) => {
if color {
TerminalColor::LightYellow.format(value)
} else {
value
}
}
Token::Version(value) => value,
Token::Status(value) => value,
Token::SectionHeader(value) => {
if color {
TerminalColor::Magenta.format(value)
} else {
value
}
}
Token::Comment(value) => {
if color {
TerminalColor::LightBlack.format(value)
} else {
value
}
}
Token::Value(value) => value,
Token::Colon(value) => value,
Token::QueryType(value) => {
if color {
TerminalColor::LightCyan.format(value)
} else {
value
}
}
Token::PredicateType(value) => {
if color {
TerminalColor::LightYellow.format(value)
} else {
value
}
}
Token::Not(value) => {
if color {
TerminalColor::LightYellow.format(value)
} else {
value
}
}
Token::Boolean(value) | Token::Number(value) => {
if color {
TerminalColor::Cyan.format(value)
} else {
value
}
}
Token::String(value) => {
if color {
TerminalColor::Green.format(value)
} else {
value
}
}
Token::Quote(value) => {
if color {
TerminalColor::Green.format(value)
} else {
value
}
}
Token::CodeDelimiter(value) => {
if color {
TerminalColor::Green.format(value)
} else {
value
}
}
Token::CodeVariable(value) => {
if color {
TerminalColor::Green.format(value)
} else {
value
}
}
Token::Keyword(value) => value,
}
}

View File

@ -1,785 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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 hurl_core::ast::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Token {
Method(String),
Version(String),
Status(String),
SectionHeader(String),
QueryType(String),
PredicateType(String),
Not(String),
Keyword(String),
// Primitives
Whitespace(String),
Comment(String),
Value(String),
Colon(String),
Quote(String),
Boolean(String),
Number(String),
String(String),
CodeDelimiter(String),
CodeVariable(String),
}
pub trait Tokenizable {
fn tokenize(&self) -> Vec<Token>;
}
fn add_tokens(tokens1: &mut Vec<Token>, tokens2: Vec<Token>) {
for token in tokens2 {
tokens1.push(token);
}
}
impl Tokenizable for HurlFile {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.entries.iter().flat_map(|e| e.tokenize()).collect(),
);
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
tokens
}
}
impl Tokenizable for Entry {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(&mut tokens, self.request.tokenize());
if let Some(response) = self.clone().response {
add_tokens(&mut tokens, response.tokenize())
}
tokens
}
}
impl Tokenizable for Request {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
tokens.push(Token::Method(self.method.as_str().to_string()));
add_tokens(&mut tokens, self.space1.tokenize());
add_tokens(&mut tokens, self.url.tokenize());
add_tokens(&mut tokens, self.line_terminator0.tokenize());
add_tokens(
&mut tokens,
self.headers.iter().flat_map(|e| e.tokenize()).collect(),
);
add_tokens(
&mut tokens,
self.sections.iter().flat_map(|e| e.tokenize()).collect(),
);
if let Some(body) = self.clone().body {
add_tokens(&mut tokens, body.tokenize())
}
tokens
}
}
impl Tokenizable for Response {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.version.tokenize());
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::Status(self.status.value.to_string()));
add_tokens(&mut tokens, self.line_terminator0.tokenize());
add_tokens(
&mut tokens,
self.headers.iter().flat_map(|e| e.tokenize()).collect(),
);
add_tokens(
&mut tokens,
self.sections.iter().flat_map(|e| e.tokenize()).collect(),
);
if let Some(body) = self.clone().body {
add_tokens(&mut tokens, body.tokenize())
}
tokens
}
}
impl Tokenizable for Version {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::Version(format!(
"HTTP/{}",
match self.value {
VersionValue::Version1 => String::from("1.0"),
VersionValue::Version11 => String::from("1.1"),
VersionValue::Version2 => String::from("2"),
VersionValue::VersionAny => String::from("*"),
}
)));
tokens
}
}
impl Tokenizable for Body {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.value.tokenize());
add_tokens(&mut tokens, self.line_terminator0.tokenize());
tokens
}
}
impl Tokenizable for Bytes {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
match self {
Bytes::Json { value } => tokens.append(&mut value.tokenize()),
Bytes::Xml { value } => {
tokens.push(Token::String(value.to_string()));
}
// Bytes::MultilineString { value: _ } => {}
Bytes::RawString { newline0, value } => {
tokens.push(Token::Keyword(String::from("```")));
add_tokens(&mut tokens, newline0.tokenize());
tokens.append(&mut value.tokenize());
tokens.push(Token::Keyword(String::from("```")));
}
Bytes::Base64 {
space0,
encoded,
space1,
..
} => {
tokens.push(Token::Keyword(String::from("base64,")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::String(encoded.to_string()));
add_tokens(&mut tokens, space1.tokenize());
tokens.push(Token::Keyword(String::from(";")));
}
Bytes::File {
space0,
filename,
space1,
} => {
tokens.push(Token::Keyword(String::from("file,")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, filename.tokenize());
//tokens.push(Token::String(filename.to_string()));
add_tokens(&mut tokens, space1.tokenize());
tokens.push(Token::Keyword(String::from(";")));
}
}
tokens
}
}
impl Tokenizable for Section {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
tokens.push(Token::SectionHeader(format!("[{}]", self.name())));
add_tokens(&mut tokens, self.line_terminator0.tokenize());
add_tokens(&mut tokens, self.value.tokenize());
// add_tokens(&mut tokens, self.space0.tokenize());
// tokens.push(Token::SectionHeader(format!("[{}]", self.name)));
tokens
}
}
impl Tokenizable for SectionValue {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
match self {
SectionValue::Asserts(items) => {
add_tokens(
&mut tokens,
items.iter().flat_map(|e| e.tokenize()).collect(),
);
}
SectionValue::QueryParams(items) => {
add_tokens(
&mut tokens,
items.iter().flat_map(|e| e.tokenize()).collect(),
);
}
SectionValue::FormParams(items) => {
add_tokens(
&mut tokens,
items.iter().flat_map(|e| e.tokenize()).collect(),
);
}
SectionValue::MultipartFormData(items) => {
add_tokens(
&mut tokens,
items.iter().flat_map(|e| e.tokenize()).collect(),
);
}
SectionValue::Cookies(items) => {
add_tokens(
&mut tokens,
items.iter().flat_map(|e| e.tokenize()).collect(),
);
}
SectionValue::Captures(items) => {
add_tokens(
&mut tokens,
items.iter().flat_map(|e| e.tokenize()).collect(),
);
}
}
tokens
}
}
impl Tokenizable for KeyValue {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.key.tokenize());
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::Colon(String::from(":")));
add_tokens(&mut tokens, self.space2.tokenize());
add_tokens(&mut tokens, self.value.tokenize());
add_tokens(&mut tokens, self.line_terminator0.tokenize());
tokens
}
}
impl Tokenizable for MultipartParam {
fn tokenize(&self) -> Vec<Token> {
match self {
MultipartParam::Param(key_value) => key_value.tokenize(),
MultipartParam::FileParam(file_param) => file_param.tokenize(),
}
}
}
impl Tokenizable for FileParam {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.key.tokenize());
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::Colon(String::from(":")));
add_tokens(&mut tokens, self.space2.tokenize());
tokens.append(&mut self.value.tokenize());
add_tokens(&mut tokens, self.line_terminator0.tokenize());
tokens
}
}
impl Tokenizable for FileValue {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::Keyword("file,".to_string()));
tokens.append(&mut self.space0.tokenize());
tokens.append(&mut self.filename.tokenize());
tokens.append(&mut self.space1.tokenize());
tokens.push(Token::Keyword(";".to_string()));
tokens.append(&mut self.space2.tokenize());
if let Some(content_type) = self.content_type.clone() {
tokens.push(Token::String(content_type));
}
tokens
}
}
impl Tokenizable for Cookie {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.name.tokenize());
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::Colon(String::from(":")));
add_tokens(&mut tokens, self.space2.tokenize());
add_tokens(&mut tokens, self.value.tokenize());
add_tokens(&mut tokens, self.line_terminator0.tokenize());
tokens
}
}
impl Tokenizable for CookieValue {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::Value(self.clone().value));
tokens
}
}
impl Tokenizable for Capture {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.name.tokenize());
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::Colon(String::from(":")));
add_tokens(&mut tokens, self.space2.tokenize());
add_tokens(&mut tokens, self.query.tokenize());
add_tokens(&mut tokens, self.space3.tokenize());
if let Some(subquery) = self.clone().subquery {
add_tokens(&mut tokens, subquery.tokenize())
}
add_tokens(&mut tokens, self.line_terminator0.tokenize());
tokens
}
}
impl Tokenizable for Assert {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(
&mut tokens,
self.line_terminators
.iter()
.flat_map(|e| e.tokenize())
.collect(),
);
add_tokens(&mut tokens, self.space0.tokenize());
add_tokens(&mut tokens, self.query.tokenize());
add_tokens(&mut tokens, self.space1.tokenize());
// TODO reconvert back your first predicate for jsonpath
// so that you can use your firstX predicate for other query
add_tokens(&mut tokens, self.predicate.tokenize());
add_tokens(&mut tokens, self.line_terminator0.tokenize());
tokens
}
}
impl Tokenizable for Query {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
match self.value.clone() {
QueryValue::Status {} => tokens.push(Token::QueryType(String::from("status"))),
QueryValue::Header { space0, name } => {
tokens.push(Token::QueryType(String::from("header")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, name.tokenize());
}
QueryValue::Cookie { space0, expr } => {
tokens.push(Token::QueryType(String::from("cookie")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::CodeDelimiter("\"".to_string()));
add_tokens(&mut tokens, expr.tokenize());
tokens.push(Token::CodeDelimiter("\"".to_string()));
}
QueryValue::Body {} => tokens.push(Token::QueryType(String::from("body"))),
QueryValue::Xpath { space0, expr } => {
tokens.push(Token::QueryType(String::from("xpath")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, expr.tokenize());
}
QueryValue::Jsonpath { space0, expr } => {
tokens.push(Token::QueryType(String::from("jsonpath")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, expr.tokenize());
}
QueryValue::Regex { space0, expr } => {
tokens.push(Token::QueryType(String::from("regex")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, expr.tokenize());
}
QueryValue::Variable { space0, name } => {
tokens.push(Token::QueryType(String::from("variable")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, name.tokenize());
}
}
tokens
}
}
impl Tokenizable for CookiePath {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(&mut tokens, self.name.tokenize());
if let Some(attribute) = self.attribute.clone() {
add_tokens(&mut tokens, attribute.tokenize());
}
tokens
}
}
impl Tokenizable for CookieAttribute {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::CodeDelimiter("[".to_string()));
add_tokens(&mut tokens, self.space0.tokenize());
tokens.push(Token::String(self.name.value()));
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::CodeDelimiter("]".to_string()));
tokens
}
}
impl Tokenizable for Subquery {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
match self.value.clone() {
SubqueryValue::Regex { space0, expr } => {
tokens.push(Token::QueryType(String::from("regex")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, expr.tokenize());
}
}
tokens
}
}
impl Tokenizable for Predicate {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
if self.not {
tokens.push(Token::Not(String::from("not")));
add_tokens(&mut tokens, self.space0.tokenize());
}
add_tokens(&mut tokens, self.predicate_func.tokenize());
tokens
}
}
impl Tokenizable for PredicateFunc {
fn tokenize(&self) -> Vec<Token> {
self.value.tokenize()
}
}
impl Tokenizable for PredicateFuncValue {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
match self {
PredicateFuncValue::EqualNull { space0 } => {
tokens.push(Token::PredicateType(String::from("equals")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Keyword("null".to_string()));
}
PredicateFuncValue::EqualBool { space0, value } => {
tokens.push(Token::PredicateType(String::from("equals")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Boolean(value.to_string()));
}
PredicateFuncValue::EqualString { space0, value } => {
tokens.push(Token::PredicateType(String::from("equals")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, value.tokenize());
}
PredicateFuncValue::EqualInt { space0, value } => {
tokens.push(Token::PredicateType(String::from("equals")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Number(value.to_string()));
}
PredicateFuncValue::EqualFloat { space0, value } => {
tokens.push(Token::PredicateType(String::from("equals")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Number(value.to_string()));
}
PredicateFuncValue::EqualExpression { space0, value } => {
tokens.push(Token::PredicateType(String::from("equals")));
add_tokens(&mut tokens, space0.tokenize());
tokens.append(&mut value.tokenize());
}
PredicateFuncValue::CountEqual { space0, value } => {
tokens.push(Token::PredicateType(String::from("countEquals")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Boolean(value.to_string()));
}
PredicateFuncValue::StartWith { space0, value } => {
tokens.push(Token::PredicateType(String::from("startsWith")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, value.tokenize());
}
PredicateFuncValue::Contain { space0, value } => {
tokens.push(Token::PredicateType(String::from("contains")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, value.tokenize());
}
PredicateFuncValue::IncludeString { space0, value } => {
tokens.push(Token::PredicateType(String::from("includes")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, value.tokenize());
}
PredicateFuncValue::IncludeInt { space0, value } => {
tokens.push(Token::PredicateType(String::from("includes")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Number(value.to_string()));
}
PredicateFuncValue::IncludeFloat { space0, value } => {
tokens.push(Token::PredicateType(String::from("includes")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Number(value.to_string()));
}
PredicateFuncValue::IncludeNull { space0 } => {
tokens.push(Token::PredicateType(String::from("includes")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Keyword("null".to_string()));
}
PredicateFuncValue::IncludeBool { space0, value } => {
tokens.push(Token::PredicateType(String::from("includes")));
add_tokens(&mut tokens, space0.tokenize());
tokens.push(Token::Boolean(value.to_string()));
}
PredicateFuncValue::IncludeExpression { space0, value } => {
tokens.push(Token::PredicateType(String::from("includes")));
add_tokens(&mut tokens, space0.tokenize());
tokens.append(&mut value.tokenize());
}
PredicateFuncValue::Match { space0, value } => {
tokens.push(Token::PredicateType(String::from("matches")));
add_tokens(&mut tokens, space0.tokenize());
add_tokens(&mut tokens, value.tokenize());
}
PredicateFuncValue::Exist {} => {
tokens.push(Token::PredicateType(String::from("exists")));
}
}
tokens
}
}
impl Tokenizable for EncodedString {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
if self.quotes {
tokens.push(Token::Quote(
if self.clone().quotes { "\"" } else { "" }.to_string(),
));
}
tokens.push(Token::String(self.encoded.clone()));
if self.quotes {
tokens.push(Token::Quote(
if self.clone().quotes { "\"" } else { "" }.to_string(),
));
}
tokens
}
}
impl Tokenizable for Template {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
if self.quotes {
tokens.push(Token::Quote(
if self.clone().quotes { "\"" } else { "" }.to_string(),
));
}
for element in self.elements.clone() {
add_tokens(&mut tokens, element.tokenize());
}
if self.quotes {
tokens.push(Token::Quote(
if self.clone().quotes { "\"" } else { "" }.to_string(),
));
}
tokens
}
}
impl Tokenizable for TemplateElement {
fn tokenize(&self) -> Vec<Token> {
match self {
TemplateElement::String { encoded, .. } => {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::String(encoded.to_string()));
tokens
}
TemplateElement::Expression(value) => {
let mut tokens: Vec<Token> = vec![];
add_tokens(&mut tokens, value.tokenize());
tokens
}
}
}
}
impl Tokenizable for Expr {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::CodeDelimiter(String::from("{{")));
add_tokens(&mut tokens, self.space0.tokenize());
tokens.push(Token::CodeVariable(self.variable.name.clone()));
add_tokens(&mut tokens, self.space1.tokenize());
tokens.push(Token::CodeDelimiter(String::from("}}")));
tokens
}
}
impl Tokenizable for LineTerminator {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
add_tokens(&mut tokens, self.space0.tokenize());
if let Some(comment) = self.clone().comment {
add_tokens(&mut tokens, comment.tokenize());
}
add_tokens(&mut tokens, self.newline.tokenize());
tokens
}
}
impl Tokenizable for Whitespace {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
if self.value != "" {
tokens.push(Token::Whitespace(self.clone().value));
}
tokens
}
}
impl Tokenizable for Comment {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::Comment(format!("#{}", self.clone().value)));
tokens
}
}
impl Tokenizable for Filename {
fn tokenize(&self) -> Vec<Token> {
return vec![Token::String(self.clone().value)];
}
}
impl Tokenizable for JsonValue {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
match self {
JsonValue::String(s) => {
//tokens.push(Token::CodeDelimiter("\"".to_string()));
tokens.append(&mut s.tokenize());
//tokens.push(Token::CodeDelimiter("\"".to_string()));
}
JsonValue::Number(value) => {
tokens.push(Token::Number(value.clone()));
}
JsonValue::Boolean(value) => {
tokens.push(Token::Number(value.to_string()));
}
JsonValue::List { space0, elements } => {
tokens.push(Token::CodeDelimiter("[".to_string()));
tokens.push(Token::Whitespace(space0.clone()));
for (i, element) in elements.iter().enumerate() {
if i > 0 {
tokens.push(Token::CodeDelimiter(",".to_string()));
}
tokens.append(&mut element.tokenize());
}
tokens.push(Token::CodeDelimiter("]".to_string()));
}
JsonValue::Object { space0, elements } => {
tokens.push(Token::CodeDelimiter("{".to_string()));
tokens.push(Token::Whitespace(space0.clone()));
for (i, element) in elements.iter().enumerate() {
if i > 0 {
tokens.push(Token::CodeDelimiter(",".to_string()));
}
tokens.append(&mut element.tokenize());
}
tokens.push(Token::CodeDelimiter("}".to_string()));
}
JsonValue::Null {} => {
tokens.push(Token::Keyword("null".to_string()));
}
}
tokens
}
}
impl Tokenizable for JsonListElement {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::Whitespace(self.space0.clone()));
tokens.append(&mut self.value.tokenize());
tokens.push(Token::Whitespace(self.space1.clone()));
tokens
}
}
impl Tokenizable for JsonObjectElement {
fn tokenize(&self) -> Vec<Token> {
let mut tokens: Vec<Token> = vec![];
tokens.push(Token::Whitespace(self.space0.clone()));
tokens.push(Token::Quote("\"".to_string()));
tokens.push(Token::String(self.name.clone()));
tokens.push(Token::Quote("\"".to_string()));
tokens.push(Token::Whitespace(self.space1.clone()));
tokens.push(Token::CodeDelimiter(":".to_string()));
tokens.push(Token::Whitespace(self.space2.clone()));
tokens.append(&mut self.value.tokenize());
tokens.push(Token::Whitespace(self.space3.clone()));
tokens
}
}

View File

@ -21,7 +21,6 @@
extern crate float_cmp;
pub mod cli;
pub mod format;
pub mod html;
pub mod http;
pub mod jsonpath;

View File

@ -29,12 +29,12 @@ use chrono::{DateTime, Local};
use clap::{AppSettings, ArgMatches};
use hurl::cli;
use hurl::cli::Error;
use hurl::html;
use hurl::http;
use hurl::runner;
use hurl::runner::{HurlResult, RunnerOptions};
use hurl_core::ast::{Pos, SourceInfo};
use hurl_core::error::Error;
use hurl_core::parser;
#[derive(Clone, Debug, PartialEq, Eq)]
@ -56,6 +56,10 @@ pub struct CLIOptions {
pub user: Option<String>,
}
pub struct CLIError {
pub message: String,
}
fn execute(
filename: &str,
contents: String,
@ -75,10 +79,12 @@ fn execute(
} else {
Some(filename.to_string())
};
let log_parser_error =
cli::make_logger_parser_error(lines.clone(), cli_options.color, optional_filename.clone());
let log_runner_error =
cli::make_logger_runner_error(lines, cli_options.color, optional_filename);
match parser::parse_hurl_file(contents.as_str()) {
Err(e) => {
log_parser_error(&e, false);
@ -89,7 +95,7 @@ fn execute(
log_verbose(format!("insecure: {}", cli_options.insecure).as_str());
log_verbose(format!("follow redirect: {}", cli_options.follow_location).as_str());
if let Some(n) = cli_options.max_redirect {
log_verbose(format!("max redirect: {}", n).as_str());
log_verbose(format!("max redirect: {}", n.to_string()).as_str());
}
if let Some(proxy) = cli_options.proxy.clone() {
log_verbose(format!("proxy: {}", proxy).as_str());
@ -108,7 +114,7 @@ fn execute(
format!(
"executing {}/{} entries",
to_entry.to_string(),
hurl_file.entries.len()
hurl_file.entries.len().to_string()
)
.as_str(),
);
@ -190,11 +196,11 @@ fn output_color(matches: ArgMatches) -> bool {
}
}
fn to_entry(matches: ArgMatches) -> Result<Option<usize>, cli::CLIError> {
fn to_entry(matches: ArgMatches) -> Result<Option<usize>, CLIError> {
match matches.value_of("to_entry") {
Some(value) => match value.parse() {
Ok(v) => Ok(Some(v)),
Err(_) => Err(cli::CLIError {
Err(_) => Err(CLIError {
message: "Invalid value for option --to-entry - must be a positive integer!"
.to_string(),
}),
@ -206,7 +212,7 @@ fn to_entry(matches: ArgMatches) -> Result<Option<usize>, cli::CLIError> {
fn json_file(
matches: ArgMatches,
log_verbose: impl Fn(&str),
) -> Result<(Vec<HurlResult>, Option<std::path::PathBuf>), cli::CLIError> {
) -> Result<(Vec<HurlResult>, Option<std::path::PathBuf>), CLIError> {
if let Some(filename) = matches.value_of("json") {
let path = Path::new(filename);
@ -217,14 +223,14 @@ fn json_file(
let v: serde_json::Value = match serde_json::from_str(data.as_str()) {
Ok(val) => val,
Err(_) => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("The file {} is not a valid json file", path.display()),
});
}
};
match runner::deserialize_results(v) {
Err(msg) => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Existing Hurl json can not be parsed! - {}", msg),
});
}
@ -240,7 +246,7 @@ fn json_file(
}
}
fn html_report(matches: ArgMatches) -> Result<Option<std::path::PathBuf>, cli::CLIError> {
fn html_report(matches: ArgMatches) -> Result<Option<std::path::PathBuf>, CLIError> {
if let Some(dir) = matches.value_of("html_report") {
let path = Path::new(dir);
if std::path::Path::new(&path).exists() {
@ -249,7 +255,7 @@ fn html_report(matches: ArgMatches) -> Result<Option<std::path::PathBuf>, cli::C
.map(|mut i| i.next().is_none())
.unwrap_or(false)
{
return Err(cli::CLIError {
return Err(CLIError {
message: format!(
"Html dir {} already exists and is not empty",
path.display()
@ -259,7 +265,7 @@ fn html_report(matches: ArgMatches) -> Result<Option<std::path::PathBuf>, cli::C
Ok(Some(path.to_path_buf()))
} else {
match std::fs::create_dir(path) {
Err(_) => Err(cli::CLIError {
Err(_) => Err(CLIError {
message: format!("Html dir {} can not be created", path.display()),
}),
Ok(_) => Ok(Some(path.to_path_buf())),
@ -270,21 +276,21 @@ fn html_report(matches: ArgMatches) -> Result<Option<std::path::PathBuf>, cli::C
}
}
fn variables(matches: ArgMatches) -> Result<HashMap<String, String>, cli::CLIError> {
fn variables(matches: ArgMatches) -> Result<HashMap<String, String>, CLIError> {
let mut variables = HashMap::new();
if matches.is_present("variable") {
let input: Vec<_> = matches.values_of("variable").unwrap().collect();
for s in input {
match s.find('=') {
None => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Missing variable value for {}!", s),
});
}
Some(index) => {
let (name, value) = s.split_at(index);
if variables.contains_key(name) {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Variable {} defined twice!", name),
});
}
@ -466,7 +472,7 @@ fn app() -> clap::App<'static, 'static> {
pub fn unwrap_or_exit<T>(
log_error_message: &impl Fn(bool, &str),
result: Result<T, cli::CLIError>,
result: Result<T, CLIError>,
) -> T {
match result {
Ok(v) => v,
@ -477,7 +483,7 @@ pub fn unwrap_or_exit<T>(
}
}
fn parse_options(matches: ArgMatches) -> Result<CLIOptions, cli::CLIError> {
fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
let verbose = matches.is_present("verbose");
let color = output_color(matches.clone());
let fail_fast = !matches.is_present("fail_at_end");
@ -494,7 +500,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, cli::CLIError> {
Some(s) => match s.parse::<usize>() {
Ok(x) => Some(x),
Err(_) => {
return Err(cli::CLIError {
return Err(CLIError {
message: "max_redirs option can not be parsed".to_string(),
});
}
@ -506,7 +512,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, cli::CLIError> {
Some(s) => match s.parse::<u64>() {
Ok(n) => Duration::from_secs(n),
Err(_) => {
return Err(cli::CLIError {
return Err(CLIError {
message: "max_time option can not be parsed".to_string(),
});
}
@ -518,7 +524,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, cli::CLIError> {
Some(s) => match s.parse::<u64>() {
Ok(n) => Duration::from_secs(n),
Err(_) => {
return Err(cli::CLIError {
return Err(CLIError {
message: "connect-timeout option can not be parsed".to_string(),
});
}
@ -586,7 +592,7 @@ fn main() {
Some(filename) => {
let filename = unwrap_or_exit(
&log_error_message,
cli::cookies_output_file(filename.to_string(), filenames.len()),
cookies_output_file(filename.to_string(), filenames.len()),
);
Some(filename)
}
@ -753,7 +759,7 @@ fn exit_code(hurl_results: Vec<HurlResult>) -> i32 {
}
}
fn write_output(bytes: Vec<u8>, filename: Option<&str>) -> Result<(), cli::CLIError> {
fn write_output(bytes: Vec<u8>, filename: Option<&str>) -> Result<(), CLIError> {
match filename {
None => {
let stdout = io::stdout();
@ -768,7 +774,7 @@ fn write_output(bytes: Vec<u8>, filename: Option<&str>) -> Result<(), cli::CLIEr
let path = Path::new(filename);
let mut file = match std::fs::File::create(&path) {
Err(why) => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", path.display(), why),
});
}
@ -781,13 +787,21 @@ fn write_output(bytes: Vec<u8>, filename: Option<&str>) -> Result<(), cli::CLIEr
}
}
fn write_cookies_file(
file_path: PathBuf,
hurl_results: Vec<HurlResult>,
) -> Result<(), cli::CLIError> {
pub fn cookies_output_file(filename: String, n: usize) -> Result<std::path::PathBuf, CLIError> {
if n > 1 {
Err(CLIError {
message: "Only save cookies for a unique session".to_string(),
})
} else {
let path = std::path::Path::new(&filename);
Ok(path.to_path_buf())
}
}
fn write_cookies_file(file_path: PathBuf, hurl_results: Vec<HurlResult>) -> Result<(), CLIError> {
let mut file = match std::fs::File::create(&file_path) {
Err(why) => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", file_path.display(), why),
});
}
@ -800,7 +814,7 @@ fn write_cookies_file(
.to_string();
match hurl_results.first() {
None => {
return Err(cli::CLIError {
return Err(CLIError {
message: "Issue fetching results".to_string(),
});
}
@ -813,17 +827,14 @@ fn write_cookies_file(
}
if let Err(why) = file.write_all(s.as_bytes()) {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", file_path.display(), why),
});
}
Ok(())
}
fn write_html_report(
dir_path: PathBuf,
hurl_results: Vec<HurlResult>,
) -> Result<(), cli::CLIError> {
fn write_html_report(dir_path: PathBuf, hurl_results: Vec<HurlResult>) -> Result<(), CLIError> {
//let now: DateTime<Utc> = Utc::now();
let now: DateTime<Local> = Local::now();
let html = create_html_index(now.to_rfc2822(), hurl_results);
@ -832,14 +843,14 @@ fn write_html_report(
let file_path = dir_path.join("index.html");
let mut file = match std::fs::File::create(&file_path) {
Err(why) => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", file_path.display(), why),
});
}
Ok(file) => file,
};
if let Err(why) = file.write_all(s.as_bytes()) {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", file_path.display(), why),
});
}
@ -847,14 +858,14 @@ fn write_html_report(
let file_path = dir_path.join("report.css");
let mut file = match std::fs::File::create(&file_path) {
Err(why) => {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", file_path.display(), why),
});
}
Ok(file) => file,
};
if let Err(why) = file.write_all(include_bytes!("report.css")) {
return Err(cli::CLIError {
return Err(CLIError {
message: format!("Issue writing to {}: {:?}", file_path.display(), why),
});
}

View File

@ -1,83 +1,7 @@
use crate::runner;
use crate::runner::RunnerError;
use hurl_core::ast::SourceInfo;
use hurl_core::parser;
use hurl_core::parser::ParseError;
pub trait Error {
fn source_info(&self) -> SourceInfo;
fn description(&self) -> String;
fn fixme(&self) -> String;
}
///
/// Textual Output for parser errors
///
impl Error for parser::Error {
fn source_info(&self) -> SourceInfo {
SourceInfo {
start: self.pos.clone(),
end: self.pos.clone(),
}
}
fn description(&self) -> String {
match self.clone().inner {
ParseError::Method { .. } => "Parsing Method".to_string(),
ParseError::Version { .. } => "Parsing Version".to_string(),
ParseError::Status { .. } => "Parsing Status".to_string(),
ParseError::Filename { .. } => "Parsing Filename".to_string(),
ParseError::Expecting { .. } => "Parsing literal".to_string(),
ParseError::Space { .. } => "Parsing space".to_string(),
ParseError::SectionName { .. } => "Parsing section name".to_string(),
ParseError::JsonpathExpr { .. } => "Parsing jsonpath expression".to_string(),
ParseError::XPathExpr { .. } => "Parsing xpath expression".to_string(),
ParseError::TemplateVariable { .. } => "Parsing template variable".to_string(),
ParseError::Json { .. } => "Parsing json".to_string(),
ParseError::Predicate { .. } => "Parsing predicate".to_string(),
ParseError::PredicateValue { .. } => "Parsing predicate value".to_string(),
ParseError::RegexExpr { .. } => "Parsing regex".to_string(),
ParseError::DuplicateSection { .. } => "Parsing section".to_string(),
ParseError::RequestSection { .. } => "Parsing section".to_string(),
ParseError::ResponseSection { .. } => "Parsing section".to_string(),
ParseError::EscapeChar { .. } => "Parsing escape character".to_string(),
ParseError::InvalidCookieAttribute { .. } => "Parsing cookie attribute".to_string(),
_ => format!("{:?}", self),
}
}
fn fixme(&self) -> String {
match self.inner.clone() {
ParseError::Method { .. } => "Available HTTP Method GET, POST, ...".to_string(),
ParseError::Version { .. } => "The http version must be 1.0, 1.1, 2 or *".to_string(),
ParseError::Status { .. } => "The http status is not valid".to_string(),
ParseError::Filename { .. } => "expecting a filename".to_string(),
ParseError::Expecting { value } => format!("expecting '{}'", value),
ParseError::Space { .. } => "expecting a space".to_string(),
ParseError::SectionName { name } => format!("the section {} is not valid", name),
ParseError::JsonpathExpr { .. } => "expecting a jsonpath expression".to_string(),
ParseError::XPathExpr { .. } => "expecting a xpath expression".to_string(),
ParseError::TemplateVariable { .. } => "expecting a variable".to_string(),
ParseError::Json { .. } => "json error".to_string(),
ParseError::Predicate { .. } => "expecting a predicate".to_string(),
ParseError::PredicateValue { .. } => "invalid predicate value".to_string(),
ParseError::RegexExpr { .. } => "Invalid Regex expression".to_string(),
ParseError::DuplicateSection { .. } => "The section is already defined".to_string(),
ParseError::RequestSection { .. } => {
"This is not a valid section for a request".to_string()
}
ParseError::ResponseSection { .. } => {
"This is not a valid section for a response".to_string()
}
ParseError::EscapeChar { .. } => "The escaping sequence is not valid".to_string(),
ParseError::InvalidCookieAttribute { .. } => {
"The cookie attribute is not valid".to_string()
}
_ => format!("{:?}", self),
}
}
}
use hurl_core::error::Error;
///
/// Textual Output for runner errors

View File

@ -34,6 +34,7 @@ mod content_decoding;
mod cookie;
mod core;
mod entry;
mod error;
mod expr;
mod http_response;
mod hurl_file;

View File

@ -15,6 +15,3 @@ float-cmp = "0.6.0"
sxd-document = "0.3.2"

View File

@ -1,9 +1,23 @@
use hurl_core::ast::SourceInfo;
use hurl_core::parser;
use hurl_core::parser::ParseError;
use crate::linter;
use crate::linter::LinterError;
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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::ast::SourceInfo;
use super::parser;
use super::parser::ParseError;
pub trait Error {
fn source_info(&self) -> SourceInfo;
@ -11,10 +25,6 @@ pub trait Error {
fn fixme(&self) -> String;
}
///
/// Textual Output for parser errors
///
impl Error for parser::Error {
fn source_info(&self) -> SourceInfo {
SourceInfo {
@ -79,29 +89,3 @@ impl Error for parser::Error {
}
}
}
///
/// Textual Output for linter errors
///
///
impl Error for linter::Error {
fn source_info(&self) -> SourceInfo {
self.clone().source_info
}
fn description(&self) -> String {
match self.inner {
LinterError::UnneccessarySpace { .. } => "Unnecessary space".to_string(),
LinterError::UnneccessaryJsonEncoding {} => "Unnecessary json encoding".to_string(),
LinterError::OneSpace {} => "One space ".to_string(),
}
}
fn fixme(&self) -> String {
match self.inner {
LinterError::UnneccessarySpace { .. } => "Remove space".to_string(),
LinterError::UnneccessaryJsonEncoding {} => "Use Simple String".to_string(),
LinterError::OneSpace {} => "Use only one space".to_string(),
}
}
}

View File

@ -20,4 +20,5 @@
extern crate float_cmp;
pub mod ast;
pub mod error;
pub mod parser;

View File

@ -16,9 +16,9 @@
*
*/
use super::error::Error;
use crate::format::TerminalColor;
use super::color::TerminalColor;
use crate::linter;
use hurl_core::error::Error;
use hurl_core::parser;
pub fn make_logger_verbose(verbose: bool) -> impl Fn(&str) {

View File

@ -16,14 +16,12 @@
*
*/
pub use self::error::Error;
pub use self::logger::{
log_info, make_logger_error_message, make_logger_linter_error, make_logger_parser_error,
make_logger_verbose,
};
pub use self::options::cookies_output_file;
pub use self::options::CLIError;
mod error;
pub use self::color::TerminalColor;
mod color;
mod logger;
mod options;

View File

@ -1,32 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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.
*
*/
pub struct CLIError {
pub message: String,
}
pub fn cookies_output_file(filename: String, n: usize) -> Result<std::path::PathBuf, CLIError> {
if n > 1 {
Err(CLIError {
message: "Only save cookies for a unique session".to_string(),
})
} else {
let path = std::path::Path::new(&filename);
Ok(path.to_path_buf())
}
}

View File

@ -16,12 +16,10 @@
*
*/
pub use self::color::TerminalColor;
pub use self::html::format as format_html;
pub use self::text::format as format_text;
pub use self::token::{Token, Tokenizable};
mod color;
mod html;
mod text;
mod token;

View File

@ -17,8 +17,8 @@
*/
use hurl_core::ast::*;
use super::color::TerminalColor;
use super::token::*;
use crate::cli::TerminalColor;
pub fn format(hurl_file: HurlFile, color: bool) -> String {
let mut buffer = String::from("");

View File

@ -1,50 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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.
*
*/
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Html {
pub head: Head,
pub body: Body,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Head {
pub title: String,
pub stylesheet: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Body {
pub children: Vec<Element>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Element {
TextElement(String),
NodeElement {
name: String,
attributes: Vec<Attribute>,
children: Vec<Element>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Attribute {
Class(String),
Id(String),
}

View File

@ -1,22 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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.
*
*/
pub use self::ast::{Attribute, Body, Element, Head, Html};
mod ast;
mod render;

View File

@ -1,131 +0,0 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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::ast::*;
impl Html {
pub fn render(self) -> String {
format!(
"<!DOCTYPE html>\n<html>{}{}</html>",
self.head.render(),
self.body.render()
)
}
}
impl Head {
fn render(self) -> String {
let mut s = "".to_string();
s.push_str(format!("<title>{}</title>", self.title).as_str());
if let Some(filename) = self.stylesheet {
s.push_str(
format!(
"<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">",
filename
)
.as_str(),
);
}
format!("<head>{}</head>", s)
}
}
impl Body {
fn render(self) -> String {
let children: Vec<String> = self.children.iter().map(|e| e.clone().render()).collect();
format!("<body>{}</body>", children.join(""))
}
}
impl Element {
fn render(self) -> String {
match self {
Element::NodeElement {
name,
children,
attributes,
} => {
let attributes = if attributes.is_empty() {
"".to_string()
} else {
format!(
" {}",
attributes
.iter()
.map(|a| a.clone().render())
.collect::<Vec<String>>()
.join("")
)
};
let children: Vec<String> = children.iter().map(|e| e.clone().render()).collect();
format!("<{}{}>{}</{}>", name, attributes, children.join(""), name)
}
Element::TextElement(s) => s,
}
}
}
impl Attribute {
fn render(self) -> String {
match self {
Attribute::Class(s) => format!("class=\"{}\"", s),
Attribute::Id(s) => format!("id=\"{}\"", s),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
pub fn sample_html() -> Html {
Html {
head: Head {
title: "This is a title".to_string(),
stylesheet: None,
},
body: Body {
children: vec![Element::NodeElement {
name: "p".to_string(),
attributes: vec![],
children: vec![Element::TextElement("Hello world!".to_string())],
}],
},
}
}
#[test]
fn test_render_html() {
assert_eq!(sample_html().render(), "<!DOCTYPE html>\n<html><head><title>This is a title</title></head><body><p>Hello world!</p></body></html>");
}
pub fn sample_div() -> Element {
Element::NodeElement {
name: "div".to_string(),
attributes: vec![Attribute::Class("request".to_string())],
children: vec![],
}
}
#[test]
fn test_render_div() {
assert_eq!(
sample_div().render(),
"<div class=\"request\"></div>".to_string()
);
}
}

View File

@ -19,5 +19,4 @@
pub mod cli;
pub mod format;
pub mod html;
pub mod linter;

View File

@ -0,0 +1,49 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 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 hurl_core::ast::SourceInfo;
use hurl_core::error::Error;
use crate::linter;
use crate::linter::LinterError;
///
/// Textual Output for linter errors
///
///
impl Error for linter::Error {
fn source_info(&self) -> SourceInfo {
self.clone().source_info
}
fn description(&self) -> String {
match self.inner {
LinterError::UnneccessarySpace { .. } => "Unnecessary space".to_string(),
LinterError::UnneccessaryJsonEncoding {} => "Unnecessary json encoding".to_string(),
LinterError::OneSpace {} => "One space ".to_string(),
}
}
fn fixme(&self) -> String {
match self.inner {
LinterError::UnneccessarySpace { .. } => "Remove space".to_string(),
LinterError::UnneccessaryJsonEncoding {} => "Use Simple String".to_string(),
LinterError::OneSpace {} => "Use only one space".to_string(),
}
}
}

View File

@ -19,4 +19,5 @@
pub use self::core::{Error, Lintable, LinterError};
mod core;
mod error;
mod rules;

View File

@ -30,6 +30,10 @@ use hurlfmt::cli;
use hurlfmt::format;
use hurlfmt::linter::Lintable;
pub struct CLIError {
pub message: String,
}
fn main() {
// // Do we have a git hash?
// // (Yes, if ripgrep was built on a machine with `git` installed.)