mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-23 09:44:22 +03:00
Make hurlfmt support several input files (like Hurl)
This commit is contained in:
parent
f7d7c6c6e7
commit
cb0ce3051a
38
integration/tests_ok/hurlfmt_multiple_input.out
Normal file
38
integration/tests_ok/hurlfmt_multiple_input.out
Normal file
@ -0,0 +1,38 @@
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 12
|
||||
[Asserts]
|
||||
header "Date" exists
|
||||
`Hello World!`
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
file, data.txt;
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
hex, 48656c6c6f20576f726c6421;
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
base64, SGVsbG8gV29ybGQh;
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 12
|
||||
[Asserts]
|
||||
header "Date" exists
|
||||
`Hello World!`
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
file, data.txt;
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
hex, 48656c6c6f20576f726c6421;
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
HTTP 200
|
||||
base64, SGVsbG8gV29ybGQh;
|
3
integration/tests_ok/hurlfmt_multiple_input.ps1
Normal file
3
integration/tests_ok/hurlfmt_multiple_input.ps1
Normal file
@ -0,0 +1,3 @@
|
||||
Set-StrictMode -Version latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
hurlfmt tests_ok/hello.hurl tests_ok/hello.hurl
|
4
integration/tests_ok/hurlfmt_multiple_input.sh
Executable file
4
integration/tests_ok/hurlfmt_multiple_input.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
hurlfmt tests_ok/hello.hurl tests_ok/hello.hurl
|
||||
|
@ -30,7 +30,19 @@ fn strip_bom(bytes: &mut Vec<u8>) {
|
||||
|
||||
/// Similar to the standard read_to_string()
|
||||
/// But remove any existing BOM
|
||||
/// Support also input stream when filename = '-'
|
||||
pub fn read_to_string(filename: &str) -> Result<String, CliError> {
|
||||
if filename == "-" {
|
||||
let mut contents = String::new();
|
||||
return if let Err(e) = std::io::stdin().read_to_string(&mut contents) {
|
||||
Err(CliError {
|
||||
message: format!("Input stream can not be read - {e}"),
|
||||
})
|
||||
} else {
|
||||
return Ok(contents);
|
||||
};
|
||||
}
|
||||
|
||||
let mut f = match File::open(filename) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
|
@ -54,12 +54,12 @@ pub fn in_place() -> clap::Arg {
|
||||
.conflicts_with("color")
|
||||
}
|
||||
|
||||
pub fn input_file() -> clap::Arg {
|
||||
clap::Arg::new("input_file")
|
||||
pub fn input_files() -> clap::Arg {
|
||||
clap::Arg::new("input_files")
|
||||
.help("Sets the input file to use")
|
||||
.required(false)
|
||||
.index(1)
|
||||
.num_args(1)
|
||||
.num_args(1..)
|
||||
}
|
||||
|
||||
pub fn input_format() -> clap::Arg {
|
||||
|
@ -70,7 +70,7 @@ pub fn in_place(arg_matches: &ArgMatches) -> Result<bool, OptionsError> {
|
||||
Err(OptionsError::Error(
|
||||
"You can use --in-place only hurl format!".to_string(),
|
||||
))
|
||||
} else if get_string(arg_matches, "input_file").is_none() {
|
||||
} else if get_string(arg_matches, "input_files").is_none() {
|
||||
Err(OptionsError::Error(
|
||||
"You can not use --in-place with standard input stream!".to_string(),
|
||||
))
|
||||
@ -82,21 +82,26 @@ pub fn in_place(arg_matches: &ArgMatches) -> Result<bool, OptionsError> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file(arg_matches: &ArgMatches) -> Result<Option<PathBuf>, OptionsError> {
|
||||
match get_string(arg_matches, "input_file") {
|
||||
None => Ok(None),
|
||||
Some(s) => {
|
||||
let path = Path::new(&s);
|
||||
/// Returns the input files from the positional arguments and input stream
|
||||
pub fn input_files(arg_matches: &ArgMatches) -> Result<Vec<String>, OptionsError> {
|
||||
let mut files = vec![];
|
||||
if let Some(filenames) = get_strings(arg_matches, "input_files") {
|
||||
for filename in filenames {
|
||||
let path = Path::new(&filename);
|
||||
if path.exists() {
|
||||
Ok(Some(path.to_path_buf()))
|
||||
files.push(filename);
|
||||
} else {
|
||||
Err(OptionsError::Error(format!(
|
||||
"input file {} does not exist",
|
||||
return Err(OptionsError::Error(format!(
|
||||
"hurl: cannot access '{}': No such file or directory",
|
||||
path.display()
|
||||
)))
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if files.is_empty() && !atty::is(Stream::Stdin) {
|
||||
files.push("-".to_string());
|
||||
}
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
pub fn output_file(arg_matches: &ArgMatches) -> Option<PathBuf> {
|
||||
@ -124,3 +129,10 @@ fn has_flag(matches: &ArgMatches, name: &str) -> bool {
|
||||
pub fn get_string(matches: &ArgMatches, name: &str) -> Option<String> {
|
||||
matches.get_one::<String>(name).map(|x| x.to_string())
|
||||
}
|
||||
|
||||
/// Returns an optional list of `String` from the command line `matches` given the option `name`.
|
||||
pub fn get_strings(matches: &ArgMatches, name: &str) -> Option<Vec<String>> {
|
||||
matches
|
||||
.get_many::<String>(name)
|
||||
.map(|v| v.map(|x| x.to_string()).collect())
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ pub struct Options {
|
||||
pub check: bool,
|
||||
pub color: bool,
|
||||
pub in_place: bool,
|
||||
pub input_file: Option<PathBuf>,
|
||||
pub input_files: Vec<String>,
|
||||
pub input_format: InputFormat,
|
||||
pub output_file: Option<PathBuf>,
|
||||
pub output_format: OutputFormat,
|
||||
@ -74,7 +74,7 @@ pub fn parse() -> Result<Options, OptionsError> {
|
||||
.arg(commands::color())
|
||||
.arg(commands::format())
|
||||
.arg(commands::in_place())
|
||||
.arg(commands::input_file())
|
||||
.arg(commands::input_files())
|
||||
.arg(commands::input_format())
|
||||
.arg(commands::no_color())
|
||||
.arg(commands::output())
|
||||
@ -84,7 +84,7 @@ pub fn parse() -> Result<Options, OptionsError> {
|
||||
let arg_matches = command.try_get_matches_from_mut(env::args_os())?;
|
||||
let opts = parse_matches(&arg_matches)?;
|
||||
|
||||
if opts.input_file.is_none() && atty::is(Stream::Stdin) {
|
||||
if opts.input_files.is_empty() && atty::is(Stream::Stdin) {
|
||||
let help = command.render_help().to_string();
|
||||
return Err(OptionsError::Error(help));
|
||||
}
|
||||
@ -95,7 +95,7 @@ fn parse_matches(arg_matches: &ArgMatches) -> Result<Options, OptionsError> {
|
||||
let check = matches::check(arg_matches);
|
||||
let color = matches::color(arg_matches);
|
||||
let in_place = matches::in_place(arg_matches)?;
|
||||
let input_file = matches::input_file(arg_matches)?;
|
||||
let input_files = matches::input_files(arg_matches)?;
|
||||
let input_format = matches::input_format(arg_matches)?;
|
||||
let output_file = matches::output_file(arg_matches);
|
||||
let output_format = matches::output_format(arg_matches)?;
|
||||
@ -104,7 +104,7 @@ fn parse_matches(arg_matches: &ArgMatches) -> Result<Options, OptionsError> {
|
||||
check,
|
||||
color,
|
||||
in_place,
|
||||
input_file,
|
||||
input_files,
|
||||
input_format,
|
||||
output_file,
|
||||
output_format,
|
||||
|
@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
|
||||
use hurl_core::parser;
|
||||
@ -51,102 +51,88 @@ fn main() {
|
||||
init_colored();
|
||||
|
||||
let log_error_message = cli::make_logger_error_message(opts.color);
|
||||
let contents = if let Some(filename) = &opts.input_file {
|
||||
match cli::read_to_string(&filename.display().to_string()) {
|
||||
Ok(s) => s,
|
||||
let mut output_all = "".to_string();
|
||||
for input_file in &opts.input_files {
|
||||
match cli::read_to_string(input_file) {
|
||||
Ok(contents) => {
|
||||
// parse input
|
||||
let input = match opts.input_format {
|
||||
InputFormat::Hurl => contents.to_string(),
|
||||
InputFormat::Curl => match curl::parse(&contents) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2);
|
||||
}
|
||||
},
|
||||
};
|
||||
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<String> = lines.iter().map(|s| (*s).to_string()).collect();
|
||||
let log_parser_error = cli::make_logger_parser_error(
|
||||
lines.clone(),
|
||||
opts.color,
|
||||
Some(input_path.clone()),
|
||||
);
|
||||
let log_linter_error =
|
||||
cli::make_logger_linter_error(lines, opts.color, Some(input_path));
|
||||
|
||||
match parser::parse_hurl_file(&input) {
|
||||
Err(e) => {
|
||||
log_parser_error(&e, false);
|
||||
process::exit(2);
|
||||
}
|
||||
Ok(hurl_file) => {
|
||||
if opts.check {
|
||||
for e in linter::check_hurl_file(&hurl_file).iter() {
|
||||
log_linter_error(e, true);
|
||||
}
|
||||
process::exit(1);
|
||||
} else {
|
||||
let output = match opts.output_format {
|
||||
OutputFormat::Hurl => {
|
||||
let hurl_file = linter::lint_hurl_file(&hurl_file);
|
||||
format::format_text(hurl_file, opts.color)
|
||||
}
|
||||
OutputFormat::Json => format::format_json(&hurl_file),
|
||||
OutputFormat::Html => {
|
||||
hurl_core::format::format_html(&hurl_file, opts.standalone)
|
||||
}
|
||||
};
|
||||
if opts.in_place {
|
||||
let output_file = Some(Path::new(input_file).to_path_buf());
|
||||
write_output(&output, output_file.clone());
|
||||
} else {
|
||||
output_all.push_str(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log_error_message(
|
||||
false,
|
||||
format!(
|
||||
"Input file {} can not be read - {}",
|
||||
filename.display(),
|
||||
e.message
|
||||
)
|
||||
.as_str(),
|
||||
format!("Input file {} can not be read - {}", input_file, e.message).as_str(),
|
||||
);
|
||||
process::exit(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut contents = String::new();
|
||||
if let Err(e) = io::stdin().read_to_string(&mut contents) {
|
||||
log_error_message(
|
||||
false,
|
||||
format!("Input stream can not be read - {e}").as_str(),
|
||||
);
|
||||
process::exit(2);
|
||||
}
|
||||
contents
|
||||
};
|
||||
|
||||
let input = match opts.input_format {
|
||||
InputFormat::Hurl => contents,
|
||||
InputFormat::Curl => match curl::parse(&contents) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
process::exit(2);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let lines: Vec<&str> = regex::Regex::new(r"\n|\r\n")
|
||||
.unwrap()
|
||||
.split(&input)
|
||||
.collect();
|
||||
let lines: Vec<String> = lines.iter().map(|s| (*s).to_string()).collect();
|
||||
let log_parser_error =
|
||||
cli::make_logger_parser_error(lines.clone(), opts.color, opts.input_file.clone());
|
||||
let log_linter_error =
|
||||
cli::make_logger_linter_error(lines, opts.color, opts.input_file.clone());
|
||||
|
||||
match parser::parse_hurl_file(&input) {
|
||||
Err(e) => {
|
||||
log_parser_error(&e, false);
|
||||
process::exit(2);
|
||||
}
|
||||
Ok(hurl_file) => {
|
||||
if opts.check {
|
||||
for e in linter::check_hurl_file(&hurl_file).iter() {
|
||||
log_linter_error(e, true);
|
||||
}
|
||||
process::exit(1);
|
||||
} else {
|
||||
let output = match opts.output_format {
|
||||
OutputFormat::Hurl => {
|
||||
let hurl_file = linter::lint_hurl_file(&hurl_file);
|
||||
format::format_text(hurl_file, opts.color)
|
||||
}
|
||||
OutputFormat::Json => format::format_json(&hurl_file),
|
||||
OutputFormat::Html => {
|
||||
hurl_core::format::format_html(&hurl_file, opts.standalone)
|
||||
}
|
||||
};
|
||||
let output = if !output.ends_with('\n') {
|
||||
format!("{output}\n")
|
||||
} else {
|
||||
output
|
||||
};
|
||||
let output_file = match opts.output_file {
|
||||
None => {
|
||||
if opts.in_place {
|
||||
Some(
|
||||
opts.input_file
|
||||
.expect("an input file when --in-place is set"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
v => v,
|
||||
};
|
||||
write_output(output.into_bytes(), output_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !opts.in_place {
|
||||
write_output(&output_all, opts.output_file);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_output(bytes: Vec<u8>, filename: Option<PathBuf>) {
|
||||
fn write_output(content: &str, filename: Option<PathBuf>) {
|
||||
let content = if !content.ends_with('\n') {
|
||||
format!("{content}\n")
|
||||
} else {
|
||||
content.to_string()
|
||||
};
|
||||
let bytes = content.into_bytes();
|
||||
match filename {
|
||||
None => {
|
||||
let stdout = io::stdout();
|
||||
@ -160,7 +146,7 @@ fn write_output(bytes: Vec<u8>, filename: Option<PathBuf>) {
|
||||
let mut file = match std::fs::File::create(&path_buf) {
|
||||
Err(why) => {
|
||||
eprintln!("Issue writing to {}: {:?}", path_buf.display(), why);
|
||||
std::process::exit(1);
|
||||
process::exit(1);
|
||||
}
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user