1use std::any::Any;
12use std::fmt;
13use std::sync::Arc;
14
15use crate::context::Context;
16use crate::error::ClickError;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub enum Nargs {
30 Count(usize),
32 Variadic,
35 Optional,
38}
39
40impl Default for Nargs {
41 fn default() -> Self {
42 Nargs::Count(1)
43 }
44}
45
46impl Nargs {
47 pub fn is_single(&self) -> bool {
49 matches!(self, Nargs::Count(1))
50 }
51
52 pub fn is_multi(&self) -> bool {
54 match self {
55 Nargs::Variadic => true,
56 Nargs::Count(n) => *n > 1,
57 Nargs::Optional => false,
58 }
59 }
60
61 pub fn is_variadic(&self) -> bool {
63 matches!(self, Nargs::Variadic)
64 }
65
66 pub fn is_optional(&self) -> bool {
68 matches!(self, Nargs::Optional)
69 }
70
71 pub fn count(&self) -> Option<usize> {
73 match self {
74 Nargs::Count(n) => Some(*n),
75 _ => None,
76 }
77 }
78}
79
80impl fmt::Display for Nargs {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 Nargs::Count(1) => write!(f, "1"),
84 Nargs::Count(n) => write!(f, "{}", n),
85 Nargs::Variadic => write!(f, "-1"),
86 Nargs::Optional => write!(f, "?"),
87 }
88 }
89}
90
91pub trait Parameter: Send + Sync + fmt::Debug {
100 fn name(&self) -> &str;
105
106 fn human_readable_name(&self) -> String;
111
112 fn nargs(&self) -> Nargs;
114
115 fn multiple(&self) -> bool;
119
120 fn is_eager(&self) -> bool;
125
126 fn expose_value(&self) -> bool;
131
132 fn required(&self) -> bool;
136
137 fn envvar(&self) -> Option<&[String]>;
143
144 fn help(&self) -> Option<&str>;
146
147 fn hidden(&self) -> bool;
149
150 fn get_metavar(&self) -> Option<String>;
154
155 fn get_help_record(&self) -> Option<(String, String)>;
160
161 fn param_type_name(&self) -> &str {
163 "parameter"
164 }
165}
166
167pub type ParameterCallback = Arc<
176 dyn Fn(
177 &Context,
178 &dyn Parameter,
179 Arc<dyn Any + Send + Sync>,
180 ) -> Result<Arc<dyn Any + Send + Sync>, ClickError>
181 + Send
182 + Sync,
183>;
184
185#[derive(Clone)]
190pub struct ParameterConfig {
191 pub name: String,
193 pub nargs: Nargs,
195 pub multiple: bool,
197 pub is_eager: bool,
199 pub expose_value: bool,
201 pub required: bool,
203 pub envvar: Option<Vec<String>>,
205 pub help: Option<String>,
207 pub hidden: bool,
209 pub metavar: Option<String>,
211 pub deprecated: Option<DeprecationInfo>,
213 pub callback: Option<ParameterCallback>,
215}
216
217impl fmt::Debug for ParameterConfig {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 f.debug_struct("ParameterConfig")
220 .field("name", &self.name)
221 .field("nargs", &self.nargs)
222 .field("multiple", &self.multiple)
223 .field("is_eager", &self.is_eager)
224 .field("expose_value", &self.expose_value)
225 .field("required", &self.required)
226 .field("envvar", &self.envvar)
227 .field("help", &self.help)
228 .field("hidden", &self.hidden)
229 .field("metavar", &self.metavar)
230 .field("deprecated", &self.deprecated)
231 .field("has_callback", &self.callback.is_some())
232 .finish()
233 }
234}
235
236#[derive(Debug, Clone, Default)]
238pub struct DeprecationInfo {
239 pub message: Option<String>,
241}
242
243impl DeprecationInfo {
244 pub fn new() -> Self {
246 Self::default()
247 }
248
249 pub fn with_message(message: impl Into<String>) -> Self {
251 Self {
252 message: Some(message.into()),
253 }
254 }
255}
256
257impl Default for ParameterConfig {
258 fn default() -> Self {
259 Self {
260 name: String::new(),
261 nargs: Nargs::default(),
262 multiple: false,
263 is_eager: false,
264 expose_value: true,
265 required: false,
266 envvar: None,
267 help: None,
268 hidden: false,
269 metavar: None,
270 deprecated: None,
271 callback: None,
272 }
273 }
274}
275
276impl ParameterConfig {
277 pub fn new(name: impl Into<String>) -> Self {
279 Self {
280 name: name.into(),
281 ..Default::default()
282 }
283 }
284
285 pub fn nargs(mut self, nargs: Nargs) -> Self {
287 self.nargs = nargs;
288 self
289 }
290
291 pub fn multiple(mut self, multiple: bool) -> Self {
293 self.multiple = multiple;
294 self
295 }
296
297 pub fn eager(mut self, eager: bool) -> Self {
299 self.is_eager = eager;
300 self
301 }
302
303 pub fn expose_value(mut self, expose: bool) -> Self {
305 self.expose_value = expose;
306 self
307 }
308
309 pub fn required(mut self, required: bool) -> Self {
311 self.required = required;
312 self
313 }
314
315 pub fn envvar(mut self, var: impl Into<String>) -> Self {
317 self.envvar = Some(vec![var.into()]);
318 self
319 }
320
321 pub fn envvars(mut self, vars: impl IntoIterator<Item = impl Into<String>>) -> Self {
323 self.envvar = Some(vars.into_iter().map(|v| v.into()).collect());
324 self
325 }
326
327 pub fn help(mut self, help: impl Into<String>) -> Self {
329 self.help = Some(help.into());
330 self
331 }
332
333 pub fn hidden(mut self, hidden: bool) -> Self {
335 self.hidden = hidden;
336 self
337 }
338
339 pub fn metavar(mut self, metavar: impl Into<String>) -> Self {
341 self.metavar = Some(metavar.into());
342 self
343 }
344
345 pub fn callback(mut self, callback: ParameterCallback) -> Self {
347 self.callback = Some(callback);
348 self
349 }
350
351 pub fn deprecated(mut self, deprecated: bool) -> Self {
353 self.deprecated = if deprecated {
354 Some(DeprecationInfo::default())
355 } else {
356 None
357 };
358 self
359 }
360
361 pub fn deprecated_with_message(mut self, message: impl Into<String>) -> Self {
363 self.deprecated = Some(DeprecationInfo::with_message(message));
364 self
365 }
366
367 pub fn validate(&self) -> Result<(), ClickError> {
371 if self.deprecated.is_some() && self.required {
373 return Err(ClickError::usage(format!(
374 "The parameter '{}' is deprecated and required. \
375 A deprecated parameter cannot be required.",
376 self.name
377 )));
378 }
379 Ok(())
380 }
381}
382
383#[cfg(test)]
388mod tests {
389 use super::*;
390
391 #[test]
392 fn test_nargs_default() {
393 let nargs = Nargs::default();
394 assert_eq!(nargs, Nargs::Count(1));
395 assert!(nargs.is_single());
396 assert!(!nargs.is_multi());
397 assert!(!nargs.is_variadic());
398 assert!(!nargs.is_optional());
399 }
400
401 #[test]
402 fn test_nargs_count() {
403 let nargs = Nargs::Count(3);
404 assert!(!nargs.is_single());
405 assert!(nargs.is_multi());
406 assert!(!nargs.is_variadic());
407 assert!(!nargs.is_optional());
408 assert_eq!(nargs.count(), Some(3));
409 }
410
411 #[test]
412 fn test_nargs_variadic() {
413 let nargs = Nargs::Variadic;
414 assert!(!nargs.is_single());
415 assert!(nargs.is_multi());
416 assert!(nargs.is_variadic());
417 assert!(!nargs.is_optional());
418 assert_eq!(nargs.count(), None);
419 }
420
421 #[test]
422 fn test_nargs_optional() {
423 let nargs = Nargs::Optional;
424 assert!(!nargs.is_single());
425 assert!(!nargs.is_multi());
426 assert!(!nargs.is_variadic());
427 assert!(nargs.is_optional());
428 assert_eq!(nargs.count(), None);
429 }
430
431 #[test]
432 fn test_nargs_display() {
433 assert_eq!(Nargs::Count(1).to_string(), "1");
434 assert_eq!(Nargs::Count(3).to_string(), "3");
435 assert_eq!(Nargs::Variadic.to_string(), "-1");
436 assert_eq!(Nargs::Optional.to_string(), "?");
437 }
438
439 #[test]
440 fn test_parameter_config_builder() {
441 let config = ParameterConfig::new("name")
442 .nargs(Nargs::Count(2))
443 .multiple(true)
444 .eager(true)
445 .expose_value(false)
446 .required(true)
447 .envvar("MY_VAR")
448 .help("Help text")
449 .hidden(false)
450 .metavar("VALUE");
451
452 assert_eq!(config.name, "name");
453 assert_eq!(config.nargs, Nargs::Count(2));
454 assert!(config.multiple);
455 assert!(config.is_eager);
456 assert!(!config.expose_value);
457 assert!(config.required);
458 assert_eq!(config.envvar, Some(vec!["MY_VAR".to_string()]));
459 assert_eq!(config.help, Some("Help text".to_string()));
460 assert!(!config.hidden);
461 assert_eq!(config.metavar, Some("VALUE".to_string()));
462 }
463
464 #[test]
465 fn test_parameter_config_envvars() {
466 let config = ParameterConfig::new("name").envvars(["VAR1", "VAR2", "VAR3"]);
467
468 assert_eq!(
469 config.envvar,
470 Some(vec![
471 "VAR1".to_string(),
472 "VAR2".to_string(),
473 "VAR3".to_string()
474 ])
475 );
476 }
477
478 #[test]
479 fn test_parameter_config_default() {
480 let config = ParameterConfig::default();
481
482 assert_eq!(config.name, "");
483 assert_eq!(config.nargs, Nargs::Count(1));
484 assert!(!config.multiple);
485 assert!(!config.is_eager);
486 assert!(config.expose_value);
487 assert!(!config.required);
488 assert!(config.envvar.is_none());
489 assert!(config.help.is_none());
490 assert!(!config.hidden);
491 assert!(config.metavar.is_none());
492 assert!(config.deprecated.is_none());
493 }
494
495 #[test]
496 fn test_parameter_config_deprecated() {
497 let config = ParameterConfig::new("old_option").deprecated(true);
498 assert!(config.deprecated.is_some());
499 assert!(config.deprecated.as_ref().unwrap().message.is_none());
500
501 let config =
502 ParameterConfig::new("old_option").deprecated_with_message("Use --new-option instead");
503 assert!(config.deprecated.is_some());
504 assert_eq!(
505 config.deprecated.as_ref().unwrap().message,
506 Some("Use --new-option instead".to_string())
507 );
508 }
509
510 #[test]
511 fn test_parameter_config_validate_deprecated_required() {
512 let config = ParameterConfig::new("option")
513 .deprecated(true)
514 .required(true);
515
516 let result = config.validate();
517 assert!(result.is_err());
518 let err = result.unwrap_err();
519 assert!(err.to_string().contains("deprecated"));
520 assert!(err.to_string().contains("required"));
521 }
522
523 #[test]
524 fn test_parameter_config_validate_ok() {
525 let config = ParameterConfig::new("option").required(true);
526
527 assert!(config.validate().is_ok());
528
529 let config = ParameterConfig::new("option").deprecated(true);
530
531 assert!(config.validate().is_ok());
532 }
533
534 #[test]
535 fn test_deprecation_info() {
536 let info = DeprecationInfo::new();
537 assert!(info.message.is_none());
538
539 let info = DeprecationInfo::with_message("Custom message");
540 assert_eq!(info.message, Some("Custom message".to_string()));
541 }
542}