1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use clap::ArgMatches;
19

            
20
use super::HurlOption;
21

            
22
45
pub fn body(arg_matches: &ArgMatches) -> Option<String> {
23
45
    match get_string(arg_matches, "data") {
24
39
        None => None,
25
6
        Some(v) => {
26
6
            if let Some(filename) = v.strip_prefix('@') {
27
3
                Some(format!("file, {filename};"))
28
            } else {
29
3
                Some(format!("```\n{v}\n```"))
30
            }
31
        }
32
    }
33
}
34

            
35
45
pub fn method(arg_matches: &ArgMatches) -> String {
36
45
    match get_string(arg_matches, "method") {
37
        None => {
38
45
            if arg_matches.contains_id("data") {
39
6
                "POST".to_string()
40
            } else {
41
39
                "GET".to_string()
42
            }
43
        }
44
        Some(v) => v,
45
    }
46
}
47

            
48
45
pub fn url(arg_matches: &ArgMatches) -> String {
49
45
    let s = if let Some(value) = get_string(arg_matches, "url") {
50
3
        value
51
    } else {
52
42
        get_string(arg_matches, "url_param").unwrap()
53
    };
54
45
    if !s.starts_with("http") {
55
        format!("https://{s}")
56
    } else {
57
45
        s
58
    }
59
}
60

            
61
45
pub fn cookies(arg_matches: &ArgMatches) -> Vec<String> {
62
45
    get_strings(arg_matches, "cookies").unwrap_or_default()
63
}
64

            
65
45
pub fn headers(arg_matches: &ArgMatches) -> Vec<String> {
66
45
    let mut headers = get_strings(arg_matches, "headers").unwrap_or_default();
67
45
    if !has_content_type(&headers) {
68
39
        if let Some(data) = get_string(arg_matches, "data") {
69
            if !data.starts_with('@') {
70
                headers.push("Content-Type: application/x-www-form-urlencoded".to_string());
71
            }
72
        }
73
    }
74

            
75
45
    headers
76
}
77

            
78
45
pub fn options(arg_matches: &ArgMatches) -> Vec<HurlOption> {
79
45
    let mut options = vec![];
80
45
    if has_flag(arg_matches, "compressed") {
81
        options.push(HurlOption::new("compressed", "true"));
82
    }
83
45
    if has_flag(arg_matches, "location") {
84
3
        options.push(HurlOption::new("location", "true"));
85
    }
86
45
    if has_flag(arg_matches, "insecure") {
87
3
        options.push(HurlOption::new("insecure", "true"));
88
    }
89
45
    if let Some(value) = get::<i32>(arg_matches, "max_redirects") {
90
        options.push(HurlOption::new("max-redirs", value.to_string().as_str()));
91
    }
92
45
    if has_flag(arg_matches, "negotiate") {
93
3
        options.push(HurlOption::new("negotiate", "true"));
94
    }
95
45
    if has_flag(arg_matches, "ntlm") {
96
3
        options.push(HurlOption::new("ntlm", "true"));
97
    }
98
45
    if let Some(value) = get::<i32>(arg_matches, "retry") {
99
3
        options.push(HurlOption::new("retry", value.to_string().as_str()));
100
    }
101
45
    if let Some(value) = get::<String>(arg_matches, "user") {
102
6
        options.push(HurlOption::new("user", value.to_string().as_str()));
103
    }
104
45
    if has_flag(arg_matches, "verbose") {
105
6
        options.push(HurlOption::new("verbose", "true"));
106
    }
107
45
    options
108
}
109

            
110
45
fn has_content_type(headers: &Vec<String>) -> bool {
111
63
    for header in headers {
112
24
        if header.starts_with("Content-Type") {
113
6
            return true;
114
        }
115
    }
116
39
    false
117
}
118

            
119
270
fn has_flag(matches: &ArgMatches, name: &str) -> bool {
120
270
    matches.get_one::<bool>(name) == Some(&true)
121
}
122

            
123
/// Returns an optional value of type `T` from the command line `matches` given the option `name`.
124
135
fn get<T: Clone + Send + Sync + 'static>(matches: &ArgMatches, name: &str) -> Option<T> {
125
135
    matches.get_one::<T>(name).cloned()
126
}
127

            
128
216
fn get_string(matches: &ArgMatches, name: &str) -> Option<String> {
129
233
    matches.get_one::<String>(name).map(|x| x.to_string())
130
}
131

            
132
/// Returns an optional list of `String` from the command line `matches` given the option `name`.
133
90
fn get_strings(matches: &ArgMatches, name: &str) -> Option<Vec<String>> {
134
90
    matches
135
90
        .get_many::<String>(name)
136
98
        .map(|v| v.map(|x| x.to_string()).collect())
137
}