mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-22 15:42:20 +03:00
Update fixme to return a StyledString
This commit is contained in:
parent
76ef2c98bc
commit
fb6379a8ba
@ -17,9 +17,9 @@
|
||||
*/
|
||||
|
||||
use crate::http::HttpError;
|
||||
use colored::Colorize;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
use hurl_core::error::DisplaySourceError;
|
||||
use hurl_core::text::{Style, StyledString};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct OutputError {
|
||||
@ -54,35 +54,22 @@ impl DisplaySourceError for OutputError {
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self, content: &[&str], color: bool) -> String {
|
||||
fn fixme(&self, content: &[&str]) -> StyledString {
|
||||
match &self.kind {
|
||||
OutputErrorKind::Http(http_error) => {
|
||||
let message = http_error.message();
|
||||
let message = hurl_core::error::add_carets(&message, self.source_info, content);
|
||||
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message
|
||||
}
|
||||
color_red(&message)
|
||||
}
|
||||
OutputErrorKind::Binary => {
|
||||
let message = "Binary output can mess up your terminal. Use \"--output -\" to tell Hurl to output it to your terminal anyway, or consider \"--output\" to save to a file.";
|
||||
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red(&message)
|
||||
}
|
||||
OutputErrorKind::Io(message) => {
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red(&message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,3 +78,9 @@ impl DisplaySourceError for OutputError {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn color_red(message: &str) -> StyledString {
|
||||
let mut s = StyledString::new();
|
||||
s.push_with(message, Style::new().red().bold());
|
||||
s
|
||||
}
|
||||
|
@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
use colored::Colorize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use hurl_core::ast::SourceInfo;
|
||||
use hurl_core::error::DisplaySourceError;
|
||||
use hurl_core::text::{Style, StyledString};
|
||||
|
||||
use crate::http::HttpError;
|
||||
|
||||
@ -150,16 +150,12 @@ impl DisplaySourceError for RunnerError {
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self, content: &[&str], color: bool) -> String {
|
||||
fn fixme(&self, content: &[&str]) -> StyledString {
|
||||
match &self.kind {
|
||||
RunnerErrorKind::AssertBodyValueError { actual, .. } => {
|
||||
let message = &format!("actual value is <{actual}>");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
color_red_multiline_string(&message)
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::AssertFailure {
|
||||
actual,
|
||||
@ -173,195 +169,110 @@ impl DisplaySourceError for RunnerError {
|
||||
""
|
||||
};
|
||||
let message = format!(" actual: {actual}\n expected: {expected}{additional}");
|
||||
if color {
|
||||
color_red_multiline_string(&message)
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::AssertHeaderValueError { actual } => {
|
||||
let message = &format!("actual value is <{actual}>");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::AssertStatus { actual, .. } => {
|
||||
let message = &format!("actual value is <{actual}>");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::AssertVersion { actual, .. } => {
|
||||
let message = &format!("actual value is <{actual}>");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
|
||||
RunnerErrorKind::FileReadAccess { path } => {
|
||||
let message = &format!("file {} can not be read", path.to_string_lossy());
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::FileWriteAccess { path, error } => {
|
||||
let message = &format!("{} can not be written ({error})", path.to_string_lossy());
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::FilterDecode(encoding) => {
|
||||
let message = &format!("value can not be decoded with <{encoding}> encoding");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::FilterInvalidEncoding(encoding) => {
|
||||
let message = &format!("<{encoding}> encoding is not supported");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::FilterInvalidInput(message) => {
|
||||
let message = &format!("invalid filter input: {message}");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::FilterMissingInput => {
|
||||
let message = "missing value to apply filter";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::Http(http_error) => {
|
||||
let message = http_error.message();
|
||||
let message = hurl_core::error::add_carets(&message, self.source_info, content);
|
||||
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::InvalidJson { value } => {
|
||||
let message = &format!("actual value is <{value}>");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::InvalidRegex => {
|
||||
let message = "regex expression is not valid";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::NoQueryResult => {
|
||||
let message = "The query didn't return any result";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::QueryHeaderNotFound => {
|
||||
let message = "this header has not been found in the response";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::QueryInvalidJson => {
|
||||
let message = "the HTTP response is not a valid JSON";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::QueryInvalidJsonpathExpression { value } => {
|
||||
let message = &format!("the JSONPath expression '{value}' is not valid");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::QueryInvalidXml => {
|
||||
let message = "the HTTP response is not a valid XML";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::QueryInvalidXpathEval => {
|
||||
let message = "the XPath expression is not valid";
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::TemplateVariableInvalidType {
|
||||
value, expecting, ..
|
||||
} => {
|
||||
let message = &format!("expecting {expecting}, actual value is <{value}>");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::TemplateVariableNotDefined { name } => {
|
||||
let message = &format!("you must set the variable {name}");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::UnauthorizedFileAccess { path } => {
|
||||
let message = &format!(
|
||||
@ -369,20 +280,12 @@ impl DisplaySourceError for RunnerError {
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
RunnerErrorKind::UnrenderableVariable { name, value } => {
|
||||
let message = &format!("variable <{name}> with value {value} can not be rendered");
|
||||
let message = hurl_core::error::add_carets(message, self.source_info, content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
color_red_multiline_string(&message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -393,28 +296,24 @@ impl DisplaySourceError for RunnerError {
|
||||
}
|
||||
|
||||
/// Color each line separately
|
||||
fn color_red_multiline_string(s: &str) -> String {
|
||||
let lines = split_lines(s);
|
||||
lines
|
||||
.iter()
|
||||
.map(|line| line.red().bold().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
/// Splits this `text` to a list of LF/CRLF separated lines.
|
||||
fn split_lines(text: &str) -> Vec<&str> {
|
||||
regex::Regex::new(r"\n|\r\n").unwrap().split(text).collect()
|
||||
fn color_red_multiline_string(s: &str) -> StyledString {
|
||||
let lines = s.split('\n');
|
||||
let mut s = StyledString::new();
|
||||
for (i, line) in lines.enumerate() {
|
||||
if i > 0 {
|
||||
s.push("\n");
|
||||
}
|
||||
s.push_with(line, Style::new().red().bold());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::http::HttpError;
|
||||
use crate::runner::{RunnerError, RunnerErrorKind};
|
||||
use hurl_core::ast::{Pos, SourceInfo};
|
||||
use hurl_core::error::{error_string, get_message};
|
||||
use hurl_core::error::{error_string, get_message, split_lines};
|
||||
|
||||
#[test]
|
||||
fn test_error_timeout() {
|
||||
|
@ -16,13 +16,14 @@
|
||||
*
|
||||
*/
|
||||
use crate::ast::SourceInfo;
|
||||
use crate::text::{Format, StyledString};
|
||||
use colored::Colorize;
|
||||
use std::cmp::max;
|
||||
|
||||
pub trait DisplaySourceError {
|
||||
fn source_info(&self) -> SourceInfo;
|
||||
fn description(&self) -> String;
|
||||
fn fixme(&self, content: &[&str], color: bool) -> String;
|
||||
fn fixme(&self, content: &[&str]) -> StyledString;
|
||||
fn show_source_line(&self) -> bool;
|
||||
}
|
||||
|
||||
@ -196,24 +197,31 @@ pub fn error_string<E: DisplaySourceError>(
|
||||
/// }
|
||||
///
|
||||
pub fn get_message<E: DisplaySourceError>(error: &E, lines: &[&str], colored: bool) -> String {
|
||||
let mut text = String::new();
|
||||
let mut text = StyledString::new();
|
||||
|
||||
if error.show_source_line() {
|
||||
let line = lines.get(error.source_info().start.line - 1).unwrap();
|
||||
let line = line.replace('\t', " ");
|
||||
text.push(' ');
|
||||
text.push_str(&line);
|
||||
text.push('\n');
|
||||
|
||||
text.push(" ");
|
||||
|
||||
text.push(&line);
|
||||
|
||||
text.push("\n");
|
||||
}
|
||||
let fixme = error.fixme(lines, colored);
|
||||
let lines = split_lines(&fixme);
|
||||
let fixme = error.fixme(lines);
|
||||
let lines = fixme.split('\n');
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
if i > 0 {
|
||||
text.push('\n');
|
||||
text.push("\n");
|
||||
}
|
||||
text.push_str(line);
|
||||
text.append(line.clone());
|
||||
}
|
||||
if colored {
|
||||
text.to_string(Format::Ansi)
|
||||
} else {
|
||||
text.to_string(Format::Plain)
|
||||
}
|
||||
text
|
||||
}
|
||||
|
||||
/// Splits this `text` to a list of LF/CRLF separated lines.
|
||||
@ -245,6 +253,7 @@ fn get_carets(line_raw: &str, error_column: usize, width: usize) -> String {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::Pos;
|
||||
use crate::text::Style;
|
||||
|
||||
#[test]
|
||||
fn test_add_carets() {
|
||||
@ -307,14 +316,14 @@ HTTP 200
|
||||
"Assert body value".to_string()
|
||||
}
|
||||
|
||||
fn fixme(&self, _lines: &[&str], _color: bool) -> String {
|
||||
r#" {
|
||||
"name": "John",
|
||||
- "age": 27
|
||||
+ "age": 28
|
||||
}
|
||||
"#
|
||||
.to_string()
|
||||
fn fixme(&self, _lines: &[&str]) -> StyledString {
|
||||
let mut diff = StyledString::new();
|
||||
diff.push(" {\n \"name\": \"John\",\n");
|
||||
diff.push_with("- \"age\": 27", Style::new().red());
|
||||
diff.push("\n");
|
||||
diff.push_with("+ \"age\": 28", Style::new().green());
|
||||
diff.push("\n }\n");
|
||||
diff
|
||||
}
|
||||
|
||||
fn show_source_line(&self) -> bool {
|
||||
@ -323,6 +332,7 @@ HTTP 200
|
||||
}
|
||||
let error = E;
|
||||
|
||||
colored::control::set_override(true);
|
||||
assert_eq!(
|
||||
get_message(&error, &split_lines(content), false),
|
||||
r#" {
|
||||
@ -332,6 +342,10 @@ HTTP 200
|
||||
}
|
||||
"#
|
||||
);
|
||||
assert_eq!(
|
||||
get_message(&error, &split_lines(content), true),
|
||||
" {\n \"name\": \"John\",\n\u{1b}[31m- \"age\": 27\u{1b}[0m\n\u{1b}[32m+ \"age\": 28\u{1b}[0m\n }\n"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
error_string(filename, content, &error, None, false),
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
use crate::ast::{Pos, SourceInfo};
|
||||
use crate::error::DisplaySourceError;
|
||||
use colored::Colorize;
|
||||
use crate::text::{Style, StyledString};
|
||||
use std::cmp;
|
||||
|
||||
/// Represents a parser error.
|
||||
@ -135,7 +135,7 @@ impl DisplaySourceError for ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self, content: &[&str], color: bool) -> String {
|
||||
fn fixme(&self, content: &[&str]) -> StyledString {
|
||||
let message = match &self.kind {
|
||||
ParseErrorKind::DuplicateSection => "the section is already defined".to_string(),
|
||||
ParseErrorKind::EscapeChar => "the escaping sequence is not valid".to_string(),
|
||||
@ -246,11 +246,9 @@ impl DisplaySourceError for ParseError {
|
||||
};
|
||||
|
||||
let message = crate::error::add_carets(&message, self.source_info(), content);
|
||||
if color {
|
||||
message.red().bold().to_string()
|
||||
} else {
|
||||
message.to_string()
|
||||
}
|
||||
let mut s = StyledString::new();
|
||||
s.push_with(&message, Style::new().red().bold());
|
||||
s
|
||||
}
|
||||
|
||||
fn show_source_line(&self) -> bool {
|
||||
|
@ -21,6 +21,7 @@ use crate::linter;
|
||||
use colored::*;
|
||||
use hurl_core::error::DisplaySourceError;
|
||||
use hurl_core::parser;
|
||||
use hurl_core::text::Format;
|
||||
|
||||
pub fn make_logger_verbose(verbose: bool) -> impl Fn(&str) {
|
||||
move |message| log_verbose(verbose, message)
|
||||
@ -101,6 +102,7 @@ fn log_error(
|
||||
} else {
|
||||
error_type.red().bold().to_string()
|
||||
};
|
||||
let format = if color { Format::Ansi } else { Format::Plain };
|
||||
eprintln!("{}: {}", error_type, error.description());
|
||||
|
||||
if let Some(filename) = filename {
|
||||
@ -131,17 +133,15 @@ fn log_error(
|
||||
// specific case for assert errors
|
||||
let lines = lines.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
|
||||
if error.source_info().start.column == 0 {
|
||||
let fix_me = &error.fixme(&lines, color);
|
||||
let fixme_lines: Vec<&str> = regex::Regex::new(r"\n|\r\n")
|
||||
.unwrap()
|
||||
.split(fix_me)
|
||||
.collect();
|
||||
let fix_me = &error.fixme(&lines);
|
||||
let fixme_lines = fix_me.split('\n');
|
||||
|
||||
// edd an empty line at the end?
|
||||
for line in fixme_lines {
|
||||
eprintln!(
|
||||
"{} | {fixme}",
|
||||
" ".repeat(line_number_size).as_str(),
|
||||
fixme = line,
|
||||
fixme = line.to_string(format),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -162,7 +162,7 @@ fn log_error(
|
||||
" ".repeat(line_number_size).as_str(),
|
||||
" ".repeat(error.source_info().start.column - 1 + tab_shift * 3),
|
||||
"^".repeat(if width > 1 { width } else { 1 }),
|
||||
fixme = error.fixme(&lines, color).as_str(),
|
||||
fixme = error.fixme(&lines).to_string(format),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
use hurl_core::ast::SourceInfo;
|
||||
use hurl_core::error::DisplaySourceError;
|
||||
use hurl_core::text::StyledString;
|
||||
|
||||
use crate::linter;
|
||||
use crate::linter::LinterError;
|
||||
@ -37,12 +38,15 @@ impl DisplaySourceError for linter::Error {
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self, _lines: &[&str], _color: bool) -> String {
|
||||
match self.inner {
|
||||
fn fixme(&self, _lines: &[&str]) -> StyledString {
|
||||
let message = match self.inner {
|
||||
LinterError::UnnecessarySpace => "Remove space".to_string(),
|
||||
LinterError::UnnecessaryJsonEncoding => "Use Simple String".to_string(),
|
||||
LinterError::OneSpace => "Use only one space".to_string(),
|
||||
}
|
||||
};
|
||||
let mut s = StyledString::new();
|
||||
s.push(&message);
|
||||
s
|
||||
}
|
||||
|
||||
fn show_source_line(&self) -> bool {
|
||||
|
Loading…
Reference in New Issue
Block a user