Lines
85.53 %
Functions
100 %
Branches
/*
* 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 std::io::{self, Write};
use std::path::PathBuf;
use std::process;
use hurl_core::input::InputKind;
use hurl_core::{parser, text};
use hurlfmt::cli::options::{InputFormat, OptionsError, OutputFormat};
use hurlfmt::cli::Logger;
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() {
text::init_crate_colored();
let opts = match cli::options::parse() {
Ok(v) => v,
Err(e) => match e {
OptionsError::Info(message) => {
print!("{message}");
process::exit(EXIT_OK);
}
OptionsError::Error(message) => {
eprintln!("{message}");
process::exit(EXIT_ERROR);
},
};
let logger = Logger::new(opts.color);
let mut output_all = String::new();
for input_file in &opts.input_files {
// Get content of the input
let content = match input_file.read_to_string() {
Ok(c) => c,
Err(e) => {
logger.error(&format!(
"Input file {} can not be read - {e}",
&input_file.to_string()
));
process::exit(EXIT_INVALID_INPUT);
// Parse input curl or Hurl file
let input = match opts.input_format {
InputFormat::Hurl => content.to_string(),
InputFormat::Curl => match curl::parse(&content) {
Ok(s) => s,
logger.error(&e.to_string());
// Parse Hurl content
let hurl_file = match parser::parse_hurl_file(&input) {
Ok(h) => h,
logger.error_parsing(&content, input_file, &e);
// Only checks
if opts.check {
let lints = linter::check_hurl_file(&hurl_file);
for e in lints.iter() {
logger.warn_lint(&content, input_file, e);
if lints.is_empty() {
} else {
process::exit(EXIT_LINT_ISSUE);
// Output files
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 InputKind::File(path) = input_file.kind() else {
unreachable!("--in-place and standard input have been filtered in args parsing")
write_output(&output, Some(path.clone()));
output_all.push_str(&output);
if !opts.in_place {
write_output(&output_all, opts.output_file);
fn write_output(content: &str, filename: Option<PathBuf>) {
let content = if !content.ends_with('\n') {
format!("{content}\n")
content.to_string()
let bytes = content.into_bytes();
match filename {
None => {
let stdout = io::stdout();
let mut handle = stdout.lock();
handle
.write_all(bytes.as_slice())
.expect("writing bytes to console");
Some(path_buf) => {
let mut file = match std::fs::File::create(&path_buf) {
Err(why) => {
eprintln!("Issue writing to {}: {:?}", path_buf.display(), why);
process::exit(1);
Ok(file) => file,
file.write_all(bytes.as_slice())
.expect("writing bytes to file");