mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-23 20:12:09 +03:00
Parse more curl options
This commit is contained in:
parent
d9fd713ab5
commit
3e37931a49
@ -15,7 +15,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
use clap::ArgAction;
|
use clap::{value_parser, ArgAction};
|
||||||
|
|
||||||
|
pub fn compressed() -> clap::Arg {
|
||||||
|
clap::Arg::new("compressed").long("compressed").num_args(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data() -> clap::Arg {
|
||||||
|
clap::Arg::new("data")
|
||||||
|
.long("data")
|
||||||
|
.short('d')
|
||||||
|
.value_name("data")
|
||||||
|
.num_args(1)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn headers() -> clap::Arg {
|
pub fn headers() -> clap::Arg {
|
||||||
clap::Arg::new("headers")
|
clap::Arg::new("headers")
|
||||||
@ -26,6 +38,29 @@ pub fn headers() -> clap::Arg {
|
|||||||
.num_args(1)
|
.num_args(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insecure() -> clap::Arg {
|
||||||
|
clap::Arg::new("insecure")
|
||||||
|
.long("insecure")
|
||||||
|
.short('k')
|
||||||
|
.num_args(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn location() -> clap::Arg {
|
||||||
|
clap::Arg::new("location")
|
||||||
|
.long("location")
|
||||||
|
.short('L')
|
||||||
|
.num_args(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_redirects() -> clap::Arg {
|
||||||
|
clap::Arg::new("max_redirects")
|
||||||
|
.long("max-redirs")
|
||||||
|
.value_name("NUM")
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.value_parser(value_parser!(i32).range(-1..))
|
||||||
|
.num_args(1)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn method() -> clap::Arg {
|
pub fn method() -> clap::Arg {
|
||||||
clap::Arg::new("method")
|
clap::Arg::new("method")
|
||||||
.long("request")
|
.long("request")
|
||||||
|
@ -17,9 +17,28 @@
|
|||||||
*/
|
*/
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
|
pub fn body(arg_matches: &ArgMatches) -> Option<String> {
|
||||||
|
match get_string(arg_matches, "data") {
|
||||||
|
None => None,
|
||||||
|
Some(v) => {
|
||||||
|
if let Some(filename) = v.strip_prefix('@') {
|
||||||
|
Some(format!("file, {filename};"))
|
||||||
|
} else {
|
||||||
|
Some(format!("```{v}```"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn method(arg_matches: &ArgMatches) -> String {
|
pub fn method(arg_matches: &ArgMatches) -> String {
|
||||||
match get_string(arg_matches, "method") {
|
match get_string(arg_matches, "method") {
|
||||||
None => "GET".to_string(),
|
None => {
|
||||||
|
if arg_matches.contains_id("data") {
|
||||||
|
"POST".to_string()
|
||||||
|
} else {
|
||||||
|
"GET".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,18 +53,62 @@ pub fn url(arg_matches: &ArgMatches) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn headers(arg_matches: &ArgMatches) -> Vec<String> {
|
pub fn headers(arg_matches: &ArgMatches) -> Vec<String> {
|
||||||
match get_strings(arg_matches, "headers") {
|
let mut headers = match get_strings(arg_matches, "headers") {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
};
|
||||||
|
if !has_content_type(&headers) {
|
||||||
|
if let Some(data) = get_string(arg_matches, "data") {
|
||||||
|
if !data.starts_with('@') {
|
||||||
|
headers.push("Content-Type: application/x-www-form-urlencoded".to_string())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_string(matches: &ArgMatches, name: &str) -> Option<String> {
|
pub fn options(arg_matches: &ArgMatches) -> Vec<String> {
|
||||||
|
let mut options = vec![];
|
||||||
|
if has_flag(arg_matches, "compressed") {
|
||||||
|
options.push("compressed: true".to_string());
|
||||||
|
}
|
||||||
|
if has_flag(arg_matches, "location") {
|
||||||
|
options.push("location: true".to_string());
|
||||||
|
}
|
||||||
|
if has_flag(arg_matches, "insecure") {
|
||||||
|
options.push("insecure: true".to_string());
|
||||||
|
}
|
||||||
|
if let Some(value) = get::<i32>(arg_matches, "max_redirects") {
|
||||||
|
options.push(format!("max-redirs: {value}"));
|
||||||
|
}
|
||||||
|
options
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_content_type(headers: &Vec<String>) -> bool {
|
||||||
|
for header in headers {
|
||||||
|
if header.starts_with("Content-Type") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_flag(matches: &ArgMatches, name: &str) -> bool {
|
||||||
|
matches.get_one::<bool>(name) == Some(&true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an optional value of type `T` from the command line `matches` given the option `name`.
|
||||||
|
fn get<T: Clone + Send + Sync + 'static>(matches: &ArgMatches, name: &str) -> Option<T> {
|
||||||
|
matches.get_one::<T>(name).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_string(matches: &ArgMatches, name: &str) -> Option<String> {
|
||||||
matches.get_one::<String>(name).map(|x| x.to_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`.
|
/// 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>> {
|
fn get_strings(matches: &ArgMatches, name: &str) -> Option<Vec<String>> {
|
||||||
matches
|
matches
|
||||||
.get_many::<String>(name)
|
.get_many::<String>(name)
|
||||||
.map(|v| v.map(|x| x.to_string()).collect())
|
.map(|v| v.map(|x| x.to_string()).collect())
|
||||||
|
@ -22,7 +22,12 @@ mod matches;
|
|||||||
|
|
||||||
pub fn parse(s: &str) -> Result<String, String> {
|
pub fn parse(s: &str) -> Result<String, String> {
|
||||||
let mut command = clap::Command::new("curl")
|
let mut command = clap::Command::new("curl")
|
||||||
|
.arg(commands::compressed())
|
||||||
|
.arg(commands::data())
|
||||||
.arg(commands::headers())
|
.arg(commands::headers())
|
||||||
|
.arg(commands::insecure())
|
||||||
|
.arg(commands::location())
|
||||||
|
.arg(commands::max_redirects())
|
||||||
.arg(commands::method())
|
.arg(commands::method())
|
||||||
.arg(commands::url());
|
.arg(commands::url());
|
||||||
|
|
||||||
@ -35,15 +40,32 @@ pub fn parse(s: &str) -> Result<String, String> {
|
|||||||
let method = matches::method(&arg_matches);
|
let method = matches::method(&arg_matches);
|
||||||
let url = matches::url(&arg_matches);
|
let url = matches::url(&arg_matches);
|
||||||
let headers = matches::headers(&arg_matches);
|
let headers = matches::headers(&arg_matches);
|
||||||
let s = format(&method, &url, headers);
|
let options = matches::options(&arg_matches);
|
||||||
|
let body = matches::body(&arg_matches);
|
||||||
|
let s = format(&method, &url, headers, options, body);
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(method: &str, url: &str, headers: Vec<String>) -> String {
|
fn format(
|
||||||
|
method: &str,
|
||||||
|
url: &str,
|
||||||
|
headers: Vec<String>,
|
||||||
|
options: Vec<String>,
|
||||||
|
body: Option<String>,
|
||||||
|
) -> String {
|
||||||
let mut s = format!("{method} {url}");
|
let mut s = format!("{method} {url}");
|
||||||
for header in headers {
|
for header in headers {
|
||||||
s.push_str(format!("\n{header}").as_str());
|
s.push_str(format!("\n{header}").as_str());
|
||||||
}
|
}
|
||||||
|
if !options.is_empty() {
|
||||||
|
s.push_str("\n[Options]");
|
||||||
|
for option in options {
|
||||||
|
s.push_str(format!("\n{option}").as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(body) = body {
|
||||||
|
s.push_str(format!("\n{body}").as_str());
|
||||||
|
}
|
||||||
s.push('\n');
|
s.push('\n');
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
@ -75,4 +97,79 @@ Test: '
|
|||||||
hurl_str
|
hurl_str
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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```
|
||||||
|
"#;
|
||||||
|
assert_eq!(
|
||||||
|
parse("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(),
|
||||||
|
hurl_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_post_json() {
|
||||||
|
let hurl_str = r#"POST http://localhost:3000/data
|
||||||
|
Content-Type: application/json
|
||||||
|
```{"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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_post_file() {
|
||||||
|
let hurl_str = r#"POST http://example.com/
|
||||||
|
file, filename;
|
||||||
|
"#;
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#"curl --data @filename http://example.com/"#).unwrap(),
|
||||||
|
hurl_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_redirect() {
|
||||||
|
let hurl_str = r#"GET http://localhost:8000/redirect-absolute
|
||||||
|
[Options]
|
||||||
|
location: true
|
||||||
|
"#;
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#"curl -L http://localhost:8000/redirect-absolute"#).unwrap(),
|
||||||
|
hurl_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insecure() {
|
||||||
|
let hurl_str = r#"GET https://localhost:8001/hello
|
||||||
|
[Options]
|
||||||
|
insecure: true
|
||||||
|
"#;
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#"curl -k https://localhost:8001/hello"#).unwrap(),
|
||||||
|
hurl_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max_redirects() {
|
||||||
|
let hurl_str = r#"GET https://localhost:8001/hello
|
||||||
|
[Options]
|
||||||
|
max-redirs: 10
|
||||||
|
"#;
|
||||||
|
assert_eq!(
|
||||||
|
parse(r#"curl https://localhost:8001/hello --max-redirs 10"#).unwrap(),
|
||||||
|
hurl_str
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user