diff --git a/integration/hurlfmt/test_lint.py b/integration/hurlfmt/test_lint.py index 88dbcba7b..fbb2c1297 100755 --- a/integration/hurlfmt/test_lint.py +++ b/integration/hurlfmt/test_lint.py @@ -20,7 +20,7 @@ def test(hurl_file): cmd = ["hurlfmt", "--check", hurl_file] print(" ".join(cmd)) result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if result.returncode != 1: + if result.returncode != 3: print(f"return code => expected: 1 actual {result.returncode}") sys.exit(1) diff --git a/integration/hurlfmt/tests_error_lint/sections.err b/integration/hurlfmt/tests_error_lint/sections.err index e69de29bb..aa1faa311 100644 --- a/integration/hurlfmt/tests_error_lint/sections.err +++ b/integration/hurlfmt/tests_error_lint/sections.err @@ -0,0 +1,7 @@ +warning: Unnecessary space + --> tests_error_lint/sections.hurl:4:1 + | + 4 | [QueryStringParams] + | ^ Remove space + | + diff --git a/integration/hurlfmt/tests_error_lint/spaces.err b/integration/hurlfmt/tests_error_lint/spaces.err index 175a68a1a..123ab694b 100644 --- a/integration/hurlfmt/tests_error_lint/spaces.err +++ b/integration/hurlfmt/tests_error_lint/spaces.err @@ -1,4 +1,4 @@ -warning: One space +warning: One space --> tests_error_lint/spaces.hurl:1:4 | 1 | GET http://localhost:8000/hello diff --git a/packages/hurlfmt/src/cli/logger.rs b/packages/hurlfmt/src/cli/logger.rs index 15eb8d6c1..11a859209 100644 --- a/packages/hurlfmt/src/cli/logger.rs +++ b/packages/hurlfmt/src/cli/logger.rs @@ -15,16 +15,12 @@ * limitations under the License. * */ -use std::path::PathBuf; - use hurl_core::error::{DisplaySourceError, OutputFormat}; use hurl_core::text::{Format, Style, StyledString}; -use crate::linter; - /// A simple logger to log app related event (start, high levels error, etc...). pub struct Logger { - /// Format of the messaeg in the terminal: ANSI or plain. + /// Format of the message in the terminal: ANSI or plain. format: Format, } @@ -44,13 +40,8 @@ impl Logger { eprintln!("{}", s.to_string(self.format)); } - /// Display a Hurl parsing error. - pub fn error_parsing_rich( - &mut self, - content: &str, - filename: &str, - error: &E, - ) { + /// Displays a Hurl parsing error. + pub fn error_parsing(&self, content: &str, filename: &str, error: &E) { // FIXME: peut-être qu'on devrait faire rentrer le prefix `error:` qui est // fournit par `self.error_rich` dans la méthode `error.to_string` let message = error.to_string( @@ -67,30 +58,21 @@ impl Logger { s.push("\n"); eprintln!("{}", s.to_string(self.format)); } -} -pub fn make_logger_linter_error( - lines: Vec, - color: bool, - filename: Option, -) -> impl Fn(&linter::Error, bool) { - move |error: &linter::Error, warning: bool| { - let filename = match &filename { - None => "-".to_string(), - Some(value) => value.display().to_string(), - }; - let content = lines.join("\n"); - let message = error.to_string(&filename, &content, None, OutputFormat::Terminal(color)); - eprintln!("{}: {}\n", get_prefix(warning, color), message); + /// Displays a lint warning. + pub fn warn_lint(&self, content: &str, filename: &str, error: &E) { + let message = error.to_string( + filename, + content, + None, + OutputFormat::Terminal(self.format == Format::Ansi), + ); + + let mut s = StyledString::new(); + s.push_with("warning", Style::new().yellow().bold()); + s.push(": "); + s.push(&message); + s.push("\n"); + eprintln!("{}", s.to_string(self.format)); } } -fn get_prefix(warning: bool, color: bool) -> String { - let mut message = StyledString::new(); - if warning { - message.push_with("warning", Style::new().yellow().bold()); - } else { - message.push_with("error", Style::new().red()); - }; - let fmt = if color { Format::Ansi } else { Format::Plain }; - message.to_string(fmt) -} diff --git a/packages/hurlfmt/src/cli/mod.rs b/packages/hurlfmt/src/cli/mod.rs index aedd93601..9fbff3972 100644 --- a/packages/hurlfmt/src/cli/mod.rs +++ b/packages/hurlfmt/src/cli/mod.rs @@ -15,9 +15,8 @@ * limitations under the License. * */ - pub use self::fs::read_to_string; -pub use self::logger::{make_logger_linter_error, Logger}; +pub use self::logger::Logger; mod fs; mod logger; diff --git a/packages/hurlfmt/src/linter/core.rs b/packages/hurlfmt/src/linter/core.rs deleted file mode 100644 index 757a2cfe7..000000000 --- a/packages/hurlfmt/src/linter/core.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Hurl (https://hurl.dev) - * Copyright (C) 2024 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; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Error { - pub source_info: SourceInfo, - pub inner: LinterError, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum LinterError { - UnnecessarySpace, - UnnecessaryJsonEncoding, - OneSpace, -} diff --git a/packages/hurlfmt/src/linter/error.rs b/packages/hurlfmt/src/linter/error.rs index 7c14460fc..2fcc752d7 100644 --- a/packages/hurlfmt/src/linter/error.rs +++ b/packages/hurlfmt/src/linter/error.rs @@ -18,36 +18,46 @@ use hurl_core::ast::SourceInfo; use hurl_core::error; use hurl_core::error::DisplaySourceError; -use hurl_core::text::StyledString; +use hurl_core::text::{Style, StyledString}; -use crate::linter; -use crate::linter::LinterError; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LinterError { + pub source_info: SourceInfo, + pub kind: LinterErrorKind, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum LinterErrorKind { + UnnecessarySpace, + UnnecessaryJsonEncoding, + OneSpace, +} /// /// Textual Output for linter errors /// -impl DisplaySourceError for linter::Error { +impl DisplaySourceError for LinterError { fn source_info(&self) -> SourceInfo { self.source_info } fn description(&self) -> String { - match self.inner { - LinterError::UnnecessarySpace => "Unnecessary space".to_string(), - LinterError::UnnecessaryJsonEncoding => "Unnecessary json encoding".to_string(), - LinterError::OneSpace => "One space ".to_string(), + match self.kind { + LinterErrorKind::UnnecessarySpace => "Unnecessary space".to_string(), + LinterErrorKind::UnnecessaryJsonEncoding => "Unnecessary json encoding".to_string(), + LinterErrorKind::OneSpace => "One space".to_string(), } } fn fixme(&self, content: &[&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 message = match self.kind { + LinterErrorKind::UnnecessarySpace => "Remove space".to_string(), + LinterErrorKind::UnnecessaryJsonEncoding => "Use Simple String".to_string(), + LinterErrorKind::OneSpace => "Use only one space".to_string(), }; let mut s = StyledString::new(); let message = error::add_carets(&message, self.source_info(), content); - s.push(&message); + s.push_with(&message, Style::new().cyan()); s } } diff --git a/packages/hurlfmt/src/linter/mod.rs b/packages/hurlfmt/src/linter/mod.rs index db71d5524..4c31eea99 100644 --- a/packages/hurlfmt/src/linter/mod.rs +++ b/packages/hurlfmt/src/linter/mod.rs @@ -17,7 +17,6 @@ */ pub use rules::{check_hurl_file, lint_hurl_file}; -pub use self::core::{Error, LinterError}; -mod core; +pub use self::error::{LinterError, LinterErrorKind}; mod error; mod rules; diff --git a/packages/hurlfmt/src/linter/rules.rs b/packages/hurlfmt/src/linter/rules.rs index 1c8a13206..bf8e91574 100644 --- a/packages/hurlfmt/src/linter/rules.rs +++ b/packages/hurlfmt/src/linter/rules.rs @@ -15,13 +15,12 @@ * limitations under the License. * */ +use crate::linter::{LinterError, LinterErrorKind}; use hurl_core::ast::*; use hurl_core::reader::Pos; -use crate::linter::core::{Error, LinterError}; - /// Returns lint errors for the `hurl_file`. -pub fn check_hurl_file(hurl_file: &HurlFile) -> Vec { +pub fn check_hurl_file(hurl_file: &HurlFile) -> Vec { hurl_file.entries.iter().flat_map(check_entry).collect() } @@ -33,7 +32,7 @@ pub fn lint_hurl_file(hurl_file: &HurlFile) -> HurlFile { } } -fn check_entry(entry: &Entry) -> Vec { +fn check_entry(entry: &Entry) -> Vec { let mut errors = vec![]; errors.append(&mut check_request(&entry.request)); match &entry.response { @@ -49,23 +48,24 @@ fn lint_entry(entry: &Entry) -> Entry { Entry { request, response } } -fn check_request(request: &Request) -> Vec { +fn check_request(request: &Request) -> Vec { let mut errors = vec![]; if !request.space0.value.is_empty() { - errors.push(Error { + errors.push(LinterError { source_info: request.space0.source_info, - inner: LinterError::UnnecessarySpace, + kind: LinterErrorKind::UnnecessarySpace, }); } if request.space1.value != " " { - errors.push(Error { + errors.push(LinterError { source_info: request.space1.source_info, - inner: LinterError::OneSpace, + kind: LinterErrorKind::OneSpace, }); } for error in check_line_terminator(&request.line_terminator0) { errors.push(error); } + errors.extend(request.sections.iter().flat_map(check_section)); errors } @@ -97,14 +97,15 @@ fn lint_request(request: &Request) -> Request { } } -fn check_response(response: &Response) -> Vec { +fn check_response(response: &Response) -> Vec { let mut errors = vec![]; if !response.space0.value.is_empty() { - errors.push(Error { + errors.push(LinterError { source_info: response.space0.source_info, - inner: LinterError::UnnecessarySpace, + kind: LinterErrorKind::UnnecessarySpace, }); } + errors.extend(response.sections.iter().flat_map(check_section)); errors } @@ -134,6 +135,20 @@ fn lint_response(response: &Response) -> Response { } } +fn check_section(section: &Section) -> Vec { + let mut errors = vec![]; + if !section.space0.value.is_empty() { + errors.push(LinterError { + source_info: section.space0.source_info, + kind: LinterErrorKind::UnnecessarySpace, + }); + } + for error in check_line_terminator(§ion.line_terminator0) { + errors.push(error); + } + errors +} + fn lint_section(section: &Section) -> Section { let line_terminators = section.line_terminators.clone(); let line_terminator0 = section.line_terminator0.clone(); @@ -568,15 +583,15 @@ fn one_whitespace() -> Whitespace { } } -fn check_line_terminator(line_terminator: &LineTerminator) -> Vec { +fn check_line_terminator(line_terminator: &LineTerminator) -> Vec { let mut errors = vec![]; match &line_terminator.comment { Some(_) => {} None => { if !line_terminator.space0.value.is_empty() { - errors.push(Error { + errors.push(LinterError { source_info: line_terminator.space0.source_info, - inner: LinterError::UnnecessarySpace, + kind: LinterErrorKind::UnnecessarySpace, }); } } diff --git a/packages/hurlfmt/src/main.rs b/packages/hurlfmt/src/main.rs index 0929cbabb..424fe7217 100644 --- a/packages/hurlfmt/src/main.rs +++ b/packages/hurlfmt/src/main.rs @@ -27,6 +27,7 @@ use hurlfmt::{cli, curl, format, linter}; const EXIT_OK: i32 = 0; const EXIT_ERROR: i32 = 1; const EXIT_INVALID_INPUT: i32 = 2; +const EXIT_LINT_ISSUE: i32 = 3; /// Executes `hurlfmt` entry point. fn main() { @@ -46,7 +47,7 @@ fn main() { }, }; - let mut logger = Logger::new(opts.color); + let logger = Logger::new(opts.color); let mut output_all = String::new(); for input_file in &opts.input_files { @@ -63,26 +64,23 @@ fn main() { } }, }; - let input_path = Path::new(input_file).to_path_buf(); - let lines: Vec<&str> = regex::Regex::new(r"\n|\r\n") - .unwrap() - .split(&input) - .collect(); - let lines: Vec = lines.iter().map(|s| (*s).to_string()).collect(); - let log_linter_error = - cli::make_logger_linter_error(lines, opts.color, Some(input_path)); match parser::parse_hurl_file(&input) { Err(e) => { - logger.error_parsing_rich(&content, input_file, &e); + logger.error_parsing(&content, input_file, &e); process::exit(EXIT_INVALID_INPUT); } Ok(hurl_file) => { if opts.check { - for e in linter::check_hurl_file(&hurl_file).iter() { - log_linter_error(e, true); + let lints = linter::check_hurl_file(&hurl_file); + for e in lints.iter() { + logger.warn_lint(&content, input_file, e); + } + if lints.is_empty() { + process::exit(EXIT_OK); + } else { + process::exit(EXIT_LINT_ISSUE); } - process::exit(1); } else { let output = match opts.output_format { OutputFormat::Hurl => {