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(&Context, &dyn Parameter, Arc<dyn Any + Send + Sync>)
177 -> Result<Arc<dyn Any + Send + Sync>, ClickError>
178 + Send
179 + Sync,
180>;
181
182#[derive(Clone)]
187pub struct ParameterConfig {
188 pub name: String,
190 pub nargs: Nargs,
192 pub multiple: bool,
194 pub is_eager: bool,
196 pub expose_value: bool,
198 pub required: bool,
200 pub envvar: Option<Vec<String>>,
202 pub help: Option<String>,
204 pub hidden: bool,
206 pub metavar: Option<String>,
208 pub deprecated: Option<DeprecationInfo>,
210 pub callback: Option<ParameterCallback>,
212}
213
214impl fmt::Debug for ParameterConfig {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 f.debug_struct("ParameterConfig")
217 .field("name", &self.name)
218 .field("nargs", &self.nargs)
219 .field("multiple", &self.multiple)
220 .field("is_eager", &self.is_eager)
221 .field("expose_value", &self.expose_value)
222 .field("required", &self.required)
223 .field("envvar", &self.envvar)
224 .field("help", &self.help)
225 .field("hidden", &self.hidden)
226 .field("metavar", &self.metavar)
227 .field("deprecated", &self.deprecated)
228 .field("has_callback", &self.callback.is_some())
229 .finish()
230 }
231}
232
233#[derive(Debug, Clone, Default)]
235pub struct DeprecationInfo {
236 pub message: Option<String>,
238}
239
240impl DeprecationInfo {
241 pub fn new() -> Self {
243 Self::default()
244 }
245
246 pub fn with_message(message: impl Into<String>) -> Self {
248 Self {
249 message: Some(message.into()),
250 }
251 }
252}
253
254impl Default for ParameterConfig {
255 fn default() -> Self {
256 Self {
257 name: String::new(),
258 nargs: Nargs::default(),
259 multiple: false,
260 is_eager: false,
261 expose_value: true,
262 required: false,
263 envvar: None,
264 help: None,
265 hidden: false,
266 metavar: None,
267 deprecated: None,
268 callback: None,
269 }
270 }
271}
272
273impl ParameterConfig {
274 pub fn new(name: impl Into<String>) -> Self {
276 Self {
277 name: name.into(),
278 ..Default::default()
279 }
280 }
281
282 pub fn nargs(mut self, nargs: Nargs) -> Self {
284 self.nargs = nargs;
285 self
286 }
287
288 pub fn multiple(mut self, multiple: bool) -> Self {
290 self.multiple = multiple;
291 self
292 }
293
294 pub fn eager(mut self, eager: bool) -> Self {
296 self.is_eager = eager;
297 self
298 }
299
300 pub fn expose_value(mut self, expose: bool) -> Self {
302 self.expose_value = expose;
303 self
304 }
305
306 pub fn required(mut self, required: bool) -> Self {
308 self.required = required;
309 self
310 }
311
312 pub fn envvar(mut self, var: impl Into<String>) -> Self {
314 self.envvar = Some(vec![var.into()]);
315 self
316 }
317
318 pub fn envvars(mut self, vars: impl IntoIterator<Item = impl Into<String>>) -> Self {
320 self.envvar = Some(vars.into_iter().map(|v| v.into()).collect());
321 self
322 }
323
324 pub fn help(mut self, help: impl Into<String>) -> Self {
326 self.help = Some(help.into());
327 self
328 }
329
330 pub fn hidden(mut self, hidden: bool) -> Self {
332 self.hidden = hidden;
333 self
334 }
335
336 pub fn metavar(mut self, metavar: impl Into<String>) -> Self {
338 self.metavar = Some(metavar.into());
339 self
340 }
341
342 pub fn callback(mut self, callback: ParameterCallback) -> Self {
344 self.callback = Some(callback);
345 self
346 }
347
348 pub fn deprecated(mut self, deprecated: bool) -> Self {
350 self.deprecated = if deprecated {
351 Some(DeprecationInfo::default())
352 } else {
353 None
354 };
355 self
356 }
357
358 pub fn deprecated_with_message(mut self, message: impl Into<String>) -> Self {
360 self.deprecated = Some(DeprecationInfo::with_message(message));
361 self
362 }
363
364 pub fn validate(&self) -> Result<(), ClickError> {
368 if self.deprecated.is_some() && self.required {
370 return Err(ClickError::usage(format!(
371 "The parameter '{}' is deprecated and required. \
372 A deprecated parameter cannot be required.",
373 self.name
374 )));
375 }
376 Ok(())
377 }
378}
379
380#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn test_nargs_default() {
390 let nargs = Nargs::default();
391 assert_eq!(nargs, Nargs::Count(1));
392 assert!(nargs.is_single());
393 assert!(!nargs.is_multi());
394 assert!(!nargs.is_variadic());
395 assert!(!nargs.is_optional());
396 }
397
398 #[test]
399 fn test_nargs_count() {
400 let nargs = Nargs::Count(3);
401 assert!(!nargs.is_single());
402 assert!(nargs.is_multi());
403 assert!(!nargs.is_variadic());
404 assert!(!nargs.is_optional());
405 assert_eq!(nargs.count(), Some(3));
406 }
407
408 #[test]
409 fn test_nargs_variadic() {
410 let nargs = Nargs::Variadic;
411 assert!(!nargs.is_single());
412 assert!(nargs.is_multi());
413 assert!(nargs.is_variadic());
414 assert!(!nargs.is_optional());
415 assert_eq!(nargs.count(), None);
416 }
417
418 #[test]
419 fn test_nargs_optional() {
420 let nargs = Nargs::Optional;
421 assert!(!nargs.is_single());
422 assert!(!nargs.is_multi());
423 assert!(!nargs.is_variadic());
424 assert!(nargs.is_optional());
425 assert_eq!(nargs.count(), None);
426 }
427
428 #[test]
429 fn test_nargs_display() {
430 assert_eq!(Nargs::Count(1).to_string(), "1");
431 assert_eq!(Nargs::Count(3).to_string(), "3");
432 assert_eq!(Nargs::Variadic.to_string(), "-1");
433 assert_eq!(Nargs::Optional.to_string(), "?");
434 }
435
436 #[test]
437 fn test_parameter_config_builder() {
438 let config = ParameterConfig::new("name")
439 .nargs(Nargs::Count(2))
440 .multiple(true)
441 .eager(true)
442 .expose_value(false)
443 .required(true)
444 .envvar("MY_VAR")
445 .help("Help text")
446 .hidden(false)
447 .metavar("VALUE");
448
449 assert_eq!(config.name, "name");
450 assert_eq!(config.nargs, Nargs::Count(2));
451 assert!(config.multiple);
452 assert!(config.is_eager);
453 assert!(!config.expose_value);
454 assert!(config.required);
455 assert_eq!(config.envvar, Some(vec!["MY_VAR".to_string()]));
456 assert_eq!(config.help, Some("Help text".to_string()));
457 assert!(!config.hidden);
458 assert_eq!(config.metavar, Some("VALUE".to_string()));
459 }
460
461 #[test]
462 fn test_parameter_config_envvars() {
463 let config = ParameterConfig::new("name").envvars(["VAR1", "VAR2", "VAR3"]);
464
465 assert_eq!(
466 config.envvar,
467 Some(vec![
468 "VAR1".to_string(),
469 "VAR2".to_string(),
470 "VAR3".to_string()
471 ])
472 );
473 }
474
475 #[test]
476 fn test_parameter_config_default() {
477 let config = ParameterConfig::default();
478
479 assert_eq!(config.name, "");
480 assert_eq!(config.nargs, Nargs::Count(1));
481 assert!(!config.multiple);
482 assert!(!config.is_eager);
483 assert!(config.expose_value);
484 assert!(!config.required);
485 assert!(config.envvar.is_none());
486 assert!(config.help.is_none());
487 assert!(!config.hidden);
488 assert!(config.metavar.is_none());
489 assert!(config.deprecated.is_none());
490 }
491
492 #[test]
493 fn test_parameter_config_deprecated() {
494 let config = ParameterConfig::new("old_option").deprecated(true);
495 assert!(config.deprecated.is_some());
496 assert!(config.deprecated.as_ref().unwrap().message.is_none());
497
498 let config =
499 ParameterConfig::new("old_option").deprecated_with_message("Use --new-option instead");
500 assert!(config.deprecated.is_some());
501 assert_eq!(
502 config.deprecated.as_ref().unwrap().message,
503 Some("Use --new-option instead".to_string())
504 );
505 }
506
507 #[test]
508 fn test_parameter_config_validate_deprecated_required() {
509 let config = ParameterConfig::new("option")
510 .deprecated(true)
511 .required(true);
512
513 let result = config.validate();
514 assert!(result.is_err());
515 let err = result.unwrap_err();
516 assert!(err.to_string().contains("deprecated"));
517 assert!(err.to_string().contains("required"));
518 }
519
520 #[test]
521 fn test_parameter_config_validate_ok() {
522 let config = ParameterConfig::new("option").required(true);
523
524 assert!(config.validate().is_ok());
525
526 let config = ParameterConfig::new("option").deprecated(true);
527
528 assert!(config.validate().is_ok());
529 }
530
531 #[test]
532 fn test_deprecation_info() {
533 let info = DeprecationInfo::new();
534 assert!(info.message.is_none());
535
536 let info = DeprecationInfo::with_message("Custom message");
537 assert_eq!(info.message, Some("Custom message".to_string()));
538 }
539}