rclip_cmd/
options_manager.rs

1//! # OptionsManager
2//!
3//! The `OptionsManager` struct represents a command line option manager.
4//!
5//! ## Usage
6//!
7//! ```rust
8//! use std::collections::HashSet;
9//! use crate::option::Option;
10//!
11//! let mut options_manager = OptionsManager::new("MyApp", Vec::new());
12//! let args = vec!["-h", "-o", "output.txt"];
13//!
14//! if let Ok(present_options) = options_manager.parse_options(args) {
15//!     if options_manager.is_present("h") {
16//!         options_manager.print_help();
17//!     } else {
18//!         for option in &options_manager.options {
19//!             // Check if the option is present
20//!             if options_manager.is_present(&option.short_name) {
21//!                 let argument = options_manager.argument(&option.short_name);
22//!                 // Process the option and its argument
23//!                 println!("Option: -{}, Argument: {}", option.short_name, argument);
24//!             }
25//!         }
26//!     }
27//! }
28//! ```
29//!
30//! ## Examples
31//!
32//! ### Creating an `OptionsManager` Instance
33//!
34//! ```rust
35//! use rclip::{Option, OptionsManager};
36//!
37//! // Create a new OptionsManager instance with default "help" option
38//! let options = vec![
39//!     Option::new("v".to_string(), "version".to_string(), false, false, "Prints version information".to_string()),
40//!     Option::new("f".to_string(), "file".to_string(), true, true, "The file to read".to_string()),
41//!     Option::new("o".to_string(), "output".to_string(), false, true, "The file to write to".to_string()),
42//! ];
43//!
44//! let mut options_manager = OptionsManager::new("MyApp", options);
45//! ```
46//!
47//! ### Parsing Command-Line Options
48//!
49//! ```rust
50//! let args = vec!["-h", "-o", "output.txt"];
51//! if let Ok(present_options) = options_manager.parse_options(args) {
52//!     // Check if the "help" option is present
53//!     if options_manager.is_present("h") {
54//!         options_manager.print_help();
55//!     } else {
56//!         for option in &options_manager.options {
57//!             // Check if the option is present
58//!             if options_manager.is_present(&option.short_name) {
59//!                 let argument = options_manager.argument(&option.short_name);
60//!                 // Process the option and its argument
61//!                 println!("Option: -{}, Argument: {}", option.short_name, argument);
62//!             }
63//!         }
64//!     }
65//! }
66//! ```
67//!
68//! ### Checking for Required Options
69//!
70//! ```rust
71//! let args = vec!["-f", "input.txt"];
72//! if let Ok(present_options) = options_manager.parse_options(args) {
73//!     for option in &options_manager.options {
74//!         // Check if a required option is missing
75//!         if option.required && !options_manager.is_present(&option.short_name) {
76//!             eprintln!("Required option -{} or --{} is missing.", option.short_name, option.long_name);
77//!         }
78//!     }
79//! }
80//! ```
81//!
82//! ## License
83//!
84//! This code is licensed under the MIT License.
85//!
86//! ## Disclaimer
87//!
88//! This code is provided as-is, and the authors make no warranties regarding its correctness or fitness for any purpose.
89//!
90//! Please feel free to report issues or submit pull requests to improve this code.
91//!
92#![doc(html_root_url = "https://docs.rs/rclip-cmd")]
93
94use std::collections::HashSet;
95use crate::option::Option;
96
97/// The OptionsManager struct represents a command line option manager.
98pub struct OptionsManager {
99    context: String,
100    options: Vec<Option>,
101    present_options: Vec<Option>,
102}
103#[warn(dead_code)]
104/// `OptionsManager` implementation.
105impl OptionsManager {
106    /// Instantiates a new `OptionsManager` instance.
107    ///
108    /// # Arguments
109    ///
110    /// * `context` - A string representing the application context.
111    /// * `options` - A vector of `Option` objects representing the available command-line options.
112    ///
113    /// # Returns
114    ///
115    /// A new `OptionsManager` instance.
116    pub fn new(context: &str, mut options: Vec<Option>) -> Self {
117        options.push(Option::new("h".to_string(), "help".to_string(), false, false, "Displays the help screen".to_string()));
118        Self { context: context.to_string(), options, present_options: Vec::new() }
119    }
120
121    /// Parses the command line options and returns option that are present.
122    ///
123    /// # Arguments
124    ///
125    /// * `args` - A vector of strings representing the command-line arguments.
126    ///
127    /// # Returns
128    ///
129    /// A `Result` containing a vector of `Option` objects representing the parsed options if successful,
130    /// or an error message as a string if parsing fails.
131    pub fn parse_options(&mut self, args: Vec<String>) -> Result<Vec<Option>, String> {
132        let mut present_options: Vec<Option> = Vec::new();
133        let mut option_set: HashSet<&str> = HashSet::new();
134        let prefix = ["-", "--"];
135
136        for option in &self.options {
137            option_set.insert(&option.short_name);
138            option_set.insert(&option.long_name);
139        }
140
141        let mut index = 0;
142        while index < args.len() {
143            let arg = &args[index];
144            if prefix.contains(&&arg[..1]) {
145                if let Some(option) = option_set.get(&arg[1..]) {
146                    let mut parsed_option = Option::new(
147                        option.to_string(),
148                        option.to_string(),
149                        false, // Initialize required as false
150                        false, // Initialize has_argument as false
151                        "".to_string(),
152                    );
153
154                    if let Some(tmp) = self.options.iter().find(|o| o.short_name == *option || o.long_name == *option) {
155                        parsed_option.required = tmp.required;
156                        parsed_option.has_argument = tmp.has_argument;
157                        parsed_option.description = tmp.description.clone();
158                    }
159
160                    if parsed_option.has_argument {
161                        index += 1;
162                        if index < args.len() {
163                            parsed_option.argument = args[index].clone();
164                        } else {
165                            return Err(format!("Option {} requires an argument.", arg));
166                        }
167                    }
168                    present_options.push(parsed_option);
169                } else {
170                    return Err(format!("Unknown option: {}", arg));
171                }
172            }
173            index += 1;
174        }
175
176        self.present_options = present_options.clone();
177
178        if self.is_present("h") {
179            self.print_help();
180        } else {
181            for option in &self.options {
182                if option.required && !present_options.iter().any(|o| o.short_name == option.short_name || o.long_name == option.long_name) {
183                    return Err(format!("Required option -{} or --{} is missing.", option.short_name, option.long_name));
184                }
185            }
186        }
187        return Ok(present_options);
188    }
189
190    /// Checks if the given option is present in the parsed options list.
191    ///
192    /// # Arguments
193    ///
194    /// * `option` - A string representing the option to check.
195    ///
196    /// # Returns
197    ///
198    /// `true` if the option is present; otherwise, `false`.
199    pub fn is_present(&self, option: &str) -> bool {
200        return self.present_options.iter().any(|o| o.short_name == option || o.long_name == option);
201    }
202
203    /// Returns the argument of the given option.
204    ///
205    /// # Arguments
206    ///
207    /// * `option` - A string representing the option to retrieve the argument for.
208    ///
209    /// # Returns
210    ///
211    /// The argument of the option as a string.
212    pub fn argument(&self, option: &str) -> String {
213        let mut argument = String::new();
214        if let Some(option) = self.present_options.iter().find(|o| o.has_argument && (o.short_name == option || o.long_name == option)) {
215            argument = option.argument.clone();
216        }
217        return argument;
218    }
219
220    /// Prints the help screen.
221    pub fn print_help(&self) {
222        println!("{} - Help: ", self.context);
223        for option in &self.options {
224            println!("  -{}, --{}{}{}", option.short_name, option.long_name, if option.has_argument { " <arg>" } else { "" }, if option.required { " (required)" } else { "" });
225            println!("      {}", option.description);
226        }
227    }
228}
229