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