1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2026 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
34
pub fn body(arg_matches: &ArgMatches) -> Option<String> {
23
34
    if let Some(v) = get_string(arg_matches, "data_raw") {
24
4
        return Some(format!("```\n{v}\n```"));
25
    }
26
30
    let v = get_string(arg_matches, "data")?;
27
4
    if let Some(filename) = v.strip_prefix('@') {
28
2
        Some(format!("file, {filename};"))
29
    } else {
30
2
        Some(format!("```\n{v}\n```"))
31
    }
32
}
33

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

            
47
36
pub fn url(arg_matches: &ArgMatches) -> Result<String, String> {
48
36
    let s = if let Some(value) = get_string(arg_matches, "url") {
49
2
        value
50
    } else {
51
34
        get_string(arg_matches, "url_param").ok_or_else(|| "URL is missing".to_string())?
52
    };
53
34
    if !s.starts_with("http") {
54
        Ok(format!("https://{s}"))
55
    } else {
56
34
        Ok(s)
57
    }
58
}
59

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

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

            
76
34
    headers
77
}
78

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

            
114
34
fn has_content_type(headers: &Vec<String>) -> bool {
115
34
    for header in headers {
116
18
        if header.starts_with("Content-Type") {
117
6
            return true;
118
        }
119
    }
120
28
    false
121
}
122

            
123
96
fn has_arg(matches: &ArgMatches, name: &str) -> bool {
124
96
    matches.get_one::<String>(name).is_some()
125
}
126

            
127
238
fn has_flag(matches: &ArgMatches, name: &str) -> bool {
128
238
    matches.get_one::<bool>(name) == Some(&true)
129
}
130

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

            
136
196
fn get_string(matches: &ArgMatches, name: &str) -> Option<String> {
137
196
    matches.get_one::<String>(name).map(|x| x.to_string())
138
}
139

            
140
/// Returns an optional list of `String` from the command line `matches` given the option `name`.
141
68
fn get_strings(matches: &ArgMatches, name: &str) -> Option<Vec<String>> {
142
68
    matches
143
68
        .get_many::<String>(name)
144
68
        .map(|v| v.map(|x| x.to_string()).collect())
145
}