mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-24 19:42:07 +03:00
Add curl input to hurlfmt
This commit is contained in:
parent
2f0fa3c016
commit
b90dbf4925
3
integration/tests_ok/import_curl.in
Normal file
3
integration/tests_ok/import_curl.in
Normal file
@ -0,0 +1,3 @@
|
||||
curl http://localhost:8000/hello
|
||||
curl http://localhost:8000/custom-headers -H 'Fruit:Raspberry' -H 'Fruit:Apple' -H 'Fruit:Banana' -H 'Fruit: Grape' -H 'Color:Green'
|
||||
curl --header 'Content-Type: application/json' --data $'{\n "name": "Bob",\n "password": "&secret\\\\\'<>",\n "age": 30,\n "strict": true,\n "spacing": "\\n",\n "g_clef": "\\uD834\\uDD1E",\n "items": [true, "true", 1],\n "variable": "\\\\"\n}' 'http://localhost:8000/post-json'
|
24
integration/tests_ok/import_curl.out
Normal file
24
integration/tests_ok/import_curl.out
Normal file
@ -0,0 +1,24 @@
|
||||
GET http://localhost:8000/hello
|
||||
|
||||
GET http://localhost:8000/custom-headers
|
||||
Fruit: Raspberry
|
||||
Fruit: Apple
|
||||
Fruit: Banana
|
||||
Fruit: Grape
|
||||
Color: Green
|
||||
|
||||
POST http://localhost:8000/post-json
|
||||
Content-Type: application/json
|
||||
```
|
||||
{
|
||||
"name": "Bob",
|
||||
"password": "&secret\\'<>",
|
||||
"age": 30,
|
||||
"strict": true,
|
||||
"spacing": "\n",
|
||||
"g_clef": "\uD834\uDD1E",
|
||||
"items": [true, "true", 1],
|
||||
"variable": "\\"
|
||||
}
|
||||
```
|
||||
|
4
integration/tests_ok/import_curl.ps1
Executable file
4
integration/tests_ok/import_curl.ps1
Executable file
@ -0,0 +1,4 @@
|
||||
Set-StrictMode -Version latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
hurl tests_ok/import_curl.out > $null # Validate expected file
|
||||
hurlfmt --in curl tests_ok/import_curl.in
|
4
integration/tests_ok/import_curl.sh
Executable file
4
integration/tests_ok/import_curl.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
hurl tests_ok/import_curl.out >/dev/null # Validate expected file
|
||||
hurlfmt --in curl tests_ok/import_curl.in
|
@ -39,6 +39,7 @@ pub fn color(arg_matches: &ArgMatches) -> bool {
|
||||
pub fn input_format(arg_matches: &ArgMatches) -> Result<InputFormat, OptionsError> {
|
||||
match get_string(arg_matches, "input_format").unwrap().as_str() {
|
||||
"hurl" => Ok(InputFormat::Hurl),
|
||||
"curl" => Ok(InputFormat::Curl),
|
||||
v => Err(OptionsError::Error(format!("Invalid input format {v}"))),
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ pub struct Options {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum InputFormat {
|
||||
Curl,
|
||||
Hurl,
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ impl Parser {
|
||||
Some('t') => '\t',
|
||||
Some('r') => '\r',
|
||||
Some(c) => c,
|
||||
_ => return Err(format!("Invalid escape at index {}", self.index)),
|
||||
_ => return Err(format!("Invalid escape at column {}", self.index + 1)),
|
||||
};
|
||||
value.push(c2);
|
||||
} else if c1 == delimiter {
|
||||
@ -104,8 +104,8 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
Err(format!(
|
||||
"Missing delimiter {delimiter} at index {}",
|
||||
self.index
|
||||
"Missing delimiter {delimiter} at column {}",
|
||||
self.index + 1
|
||||
))
|
||||
} else {
|
||||
loop {
|
||||
@ -114,7 +114,7 @@ impl Parser {
|
||||
if let Some(c) = self.read() {
|
||||
value.push(c);
|
||||
} else {
|
||||
return Err(format!("Invalid escape at index {}", self.index));
|
||||
return Err(format!("Invalid escape at column {}", self.index + 1));
|
||||
}
|
||||
}
|
||||
Some(' ') => return Ok(Some(value)),
|
||||
@ -150,7 +150,7 @@ mod test {
|
||||
fn test_split_error() {
|
||||
assert_eq!(
|
||||
args::split(r#"AAA 'BBB"#).err().unwrap(),
|
||||
"Missing delimiter ' at index 8".to_string()
|
||||
"Missing delimiter ' at column 9".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ mod test {
|
||||
let mut parser = Parser::new("'value");
|
||||
assert_eq!(
|
||||
parser.param().err().unwrap(),
|
||||
"Missing delimiter ' at index 6".to_string()
|
||||
"Missing delimiter ' at column 7".to_string()
|
||||
);
|
||||
assert_eq!(parser.index, 6);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ pub fn body(arg_matches: &ArgMatches) -> Option<String> {
|
||||
if let Some(filename) = v.strip_prefix('@') {
|
||||
Some(format!("file, {filename};"))
|
||||
} else {
|
||||
Some(format!("```{v}```"))
|
||||
Some(format!("```\n{v}\n```"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,22 @@ mod commands;
|
||||
mod matches;
|
||||
|
||||
pub fn parse(s: &str) -> Result<String, String> {
|
||||
let lines: Vec<&str> = regex::Regex::new(r"\n|\r\n")
|
||||
.unwrap()
|
||||
.split(s)
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
let mut s = "".to_string();
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let hurl_str = parse_line(line).map_err(|message| {
|
||||
format!("Can not parse curl command at line {}: {message}", i + 1)
|
||||
})?;
|
||||
s.push_str(format!("{hurl_str}\n").as_str())
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn parse_line(s: &str) -> Result<String, String> {
|
||||
let mut command = clap::Command::new("curl")
|
||||
.arg(commands::compressed())
|
||||
.arg(commands::data())
|
||||
@ -64,7 +80,8 @@ fn format(
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
s.push_str(format!("\n{body}").as_str());
|
||||
s.push('\n');
|
||||
s.push_str(body.as_str());
|
||||
}
|
||||
s.push('\n');
|
||||
s
|
||||
@ -72,13 +89,35 @@ fn format(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::curl::parse;
|
||||
use crate::curl::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let hurl_str = r#"GET http://localhost:8000/hello
|
||||
|
||||
GET http://localhost:8000/custom-headers
|
||||
Fruit:Raspberry
|
||||
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(
|
||||
r#"curl http://localhost:8000/hello
|
||||
curl http://localhost:8000/custom-headers -H 'Fruit:Raspberry'
|
||||
"#
|
||||
)
|
||||
.unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hello() {
|
||||
let hurl_str = r#"GET http://locahost:8000/hello
|
||||
let hurl_str = r#"GET http://localhost:8000/hello
|
||||
"#;
|
||||
assert_eq!(parse("curl http://locahost:8000/hello").unwrap(), hurl_str);
|
||||
assert_eq!(
|
||||
parse_line("curl http://localhost:8000/hello").unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -89,11 +128,25 @@ Fruit: Banana
|
||||
Test: '
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse("curl http://localhost:8000/custom-headers -H 'Fruit:Raspberry' -H 'Fruit: Banana' -H $'Test: \\''").unwrap(),
|
||||
parse_line("curl http://localhost:8000/custom-headers -H 'Fruit:Raspberry' -H 'Fruit: Banana' -H $'Test: \\''").unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
assert_eq!(
|
||||
parse("curl http://localhost:8000/custom-headers --header Fruit:Raspberry -H 'Fruit: Banana' -H $'Test: \\'' ").unwrap(),
|
||||
parse_line("curl http://localhost:8000/custom-headers --header Fruit:Raspberry -H 'Fruit: Banana' -H $'Test: \\'' ").unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_post_hello() {
|
||||
let hurl_str = r#"POST http://localhost:8000/hello
|
||||
Content-Type: text/plain
|
||||
```
|
||||
hello
|
||||
```
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_line(r#"curl -d $'hello' -H 'Content-Type: text/plain' -X POST http://localhost:8000/hello"#).unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
@ -102,14 +155,16 @@ Test: '
|
||||
fn test_post_format_params() {
|
||||
let hurl_str = r#"POST http://localhost:3000/data
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
```param1=value1¶m2=value2```
|
||||
```
|
||||
param1=value1¶m2=value2
|
||||
```
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse("curl http://localhost:3000/data -d 'param1=value1¶m2=value2'").unwrap(),
|
||||
parse_line("curl http://localhost:3000/data -d 'param1=value1¶m2=value2'").unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
assert_eq!(
|
||||
parse("curl -X POST http://localhost:3000/data -H 'Content-Type: application/x-www-form-urlencoded' --data 'param1=value1¶m2=value2'").unwrap(),
|
||||
parse_line("curl -X POST http://localhost:3000/data -H 'Content-Type: application/x-www-form-urlencoded' --data 'param1=value1¶m2=value2'").unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
@ -118,23 +173,26 @@ Content-Type: application/x-www-form-urlencoded
|
||||
fn test_post_json() {
|
||||
let hurl_str = r#"POST http://localhost:3000/data
|
||||
Content-Type: application/json
|
||||
```{"key1":"value1", "key2":"value2"}```
|
||||
```
|
||||
{"key1":"value1", "key2":"value2"}
|
||||
```
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(r#"curl -d '{"key1":"value1", "key2":"value2"}' -H 'Content-Type: application/json' -X POST http://localhost:3000/data"#).unwrap(),
|
||||
hurl_str
|
||||
hurl_str,
|
||||
parse_line(r#"curl -d '{"key1":"value1", "key2":"value2"}' -H 'Content-Type: application/json' -X POST http://localhost:3000/data"#).unwrap()
|
||||
);
|
||||
|
||||
let hurl_str = r#"POST http://localhost:3000/data
|
||||
Content-Type: application/json
|
||||
```{
|
||||
```
|
||||
{
|
||||
"key1": "value1",
|
||||
"key2": "value2"
|
||||
}
|
||||
```
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(r#"curl -d $'{\n "key1": "value1",\n "key2": "value2"\n}\n' -H 'Content-Type: application/json' -X POST http://localhost:3000/data"#).unwrap(),
|
||||
parse_line(r#"curl -d $'{\n "key1": "value1",\n "key2": "value2"\n}' -H 'Content-Type: application/json' -X POST http://localhost:3000/data"#).unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
@ -145,7 +203,7 @@ Content-Type: application/json
|
||||
file, filename;
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(r#"curl --data @filename http://example.com/"#).unwrap(),
|
||||
parse_line(r#"curl --data @filename http://example.com/"#).unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
@ -157,7 +215,7 @@ file, filename;
|
||||
location: true
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(r#"curl -L http://localhost:8000/redirect-absolute"#).unwrap(),
|
||||
parse_line(r#"curl -L http://localhost:8000/redirect-absolute"#).unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
@ -169,7 +227,7 @@ location: true
|
||||
insecure: true
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(r#"curl -k https://localhost:8001/hello"#).unwrap(),
|
||||
parse_line(r#"curl -k https://localhost:8001/hello"#).unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
@ -181,7 +239,7 @@ insecure: true
|
||||
max-redirs: 10
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse(r#"curl https://localhost:8001/hello --max-redirs 10"#).unwrap(),
|
||||
parse_line(r#"curl https://localhost:8001/hello --max-redirs 10"#).unwrap(),
|
||||
hurl_str
|
||||
);
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use hurl_core::parser;
|
||||
use hurlfmt::cli::options::{OptionsError, OutputFormat};
|
||||
use hurlfmt::{cli, format, linter};
|
||||
use hurlfmt::cli::options::{InputFormat, OptionsError, OutputFormat};
|
||||
use hurlfmt::{cli, curl, format, linter};
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
pub fn init_colored() {
|
||||
@ -80,17 +80,28 @@ fn main() {
|
||||
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(&contents)
|
||||
.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(&contents) {
|
||||
|
||||
match parser::parse_hurl_file(&input) {
|
||||
Err(e) => {
|
||||
log_parser_error(&e, false);
|
||||
process::exit(2);
|
||||
|
Loading…
Reference in New Issue
Block a user