1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 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 std::io;
19
use std::io::IsTerminal;
20
use std::path::{Path, PathBuf};
21

            
22
use clap::parser::ValueSource;
23
use clap::ArgMatches;
24
use hurl_core::input::Input;
25

            
26
use super::OptionsError;
27
use crate::cli::options::{InputFormat, OutputFormat};
28

            
29
189
pub fn check(arg_matches: &ArgMatches) -> bool {
30
189
    has_flag(arg_matches, "check")
31
}
32

            
33
189
pub fn color(arg_matches: &ArgMatches) -> bool {
34
189
    if has_flag(arg_matches, "color") {
35
6
        true
36
183
    } else if has_flag(arg_matches, "no_color") || has_flag(arg_matches, "in_place") {
37
3
        false
38
    } else {
39
180
        io::stdout().is_terminal()
40
    }
41
}
42

            
43
189
pub fn input_format(arg_matches: &ArgMatches) -> Result<InputFormat, OptionsError> {
44
189
    match get_string(arg_matches, "input_format").unwrap().as_str() {
45
189
        "hurl" => Ok(InputFormat::Hurl),
46
3
        "curl" => Ok(InputFormat::Curl),
47
        v => Err(OptionsError::Error(format!("Invalid input format {v}"))),
48
    }
49
}
50

            
51
189
pub fn output_format(arg_matches: &ArgMatches) -> Result<OutputFormat, OptionsError> {
52
189
    // Deprecated --format option
53
189
    if arg_matches.value_source("format") == Some(ValueSource::CommandLine) {
54
        eprintln!("--format is deprecated. use --out instead.");
55
        return match get_string(arg_matches, "format").unwrap().as_str() {
56
            "hurl" => Ok(OutputFormat::Hurl),
57
            "json" => Ok(OutputFormat::Json),
58
            "html" => Ok(OutputFormat::Html),
59
            v => Err(OptionsError::Error(format!("Invalid output format {v}"))),
60
        };
61
    }
62
189

            
63
189
    match get_string(arg_matches, "output_format").unwrap().as_str() {
64
189
        "hurl" => Ok(OutputFormat::Hurl),
65
105
        "json" => Ok(OutputFormat::Json),
66
54
        "html" => Ok(OutputFormat::Html),
67
        v => Err(OptionsError::Error(format!("Invalid output format {v}"))),
68
    }
69
}
70

            
71
189
pub fn in_place(arg_matches: &ArgMatches) -> Result<bool, OptionsError> {
72
189
    if has_flag(arg_matches, "in_place") {
73
3
        if get_string(arg_matches, "input_format") != Some("hurl".to_string()) {
74
            Err(OptionsError::Error(
75
                "You can use --in-place only hurl format!".to_string(),
76
            ))
77
3
        } else if get_string(arg_matches, "input_files").is_none() {
78
            Err(OptionsError::Error(
79
                "You can not use --in-place with standard input stream!".to_string(),
80
            ))
81
        } else {
82
3
            Ok(true)
83
        }
84
    } else {
85
186
        Ok(false)
86
    }
87
}
88

            
89
/// Returns the input files from the positional arguments and input stream
90
189
pub fn input_files(arg_matches: &ArgMatches) -> Result<Vec<Input>, OptionsError> {
91
189
    let mut files = vec![];
92
189
    if let Some(filenames) = get_strings(arg_matches, "input_files") {
93
375
        for filename in &filenames {
94
189
            let filename = Path::new(filename);
95
189
            if !filename.exists() {
96
                return Err(OptionsError::Error(format!(
97
                    "error: Cannot access '{}': No such file or directory",
98
                    filename.display()
99
                )));
100
            }
101
189
            let file = Input::from(filename);
102
189
            files.push(file);
103
        }
104
    }
105
189
    if files.is_empty() && !io::stdin().is_terminal() {
106
3
        let input = match Input::from_stdin() {
107
3
            Ok(input) => input,
108
            Err(err) => return Err(OptionsError::Error(err.to_string())),
109
        };
110
3
        files.push(input);
111
    }
112
189
    Ok(files)
113
}
114

            
115
189
pub fn output_file(arg_matches: &ArgMatches) -> Option<PathBuf> {
116
189
    get_string(arg_matches, "output").map(|s| Path::new(&s).to_path_buf())
117
}
118

            
119
189
pub fn standalone(arg_matches: &ArgMatches) -> Result<bool, OptionsError> {
120
189
    if has_flag(arg_matches, "standalone") {
121
3
        if get_string(arg_matches, "output_format") != Some("html".to_string()) {
122
            Err(OptionsError::Error(
123
                "use --standalone option only with html output".to_string(),
124
            ))
125
        } else {
126
3
            Ok(true)
127
        }
128
    } else {
129
186
        Ok(false)
130
    }
131
}
132

            
133
1122
fn has_flag(matches: &ArgMatches, name: &str) -> bool {
134
1122
    matches.get_one::<bool>(name) == Some(&true)
135
}
136

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

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