1// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>, 2// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and 3// Ana Hobden (@hoverbear) <operator@hoverbear.org> 4// 5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license 7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your 8// option. This file may not be copied, modified, or distributed 9// except according to those terms. 10// 11// This work was derived from Structopt (https://github.com/TeXitoi/structopt) 12// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the 13// MIT/Apache 2.0 license. 14 15#![allow(clippy::option_option)] 16 17use crate::utils; 18 19use clap::{Parser, Subcommand}; 20 21#[test] 22fn required_option() { 23 #[derive(Parser, PartialEq, Debug)] 24 #[command(args_override_self = true)] 25 struct Opt { 26 #[arg(short, long)] 27 arg: i32, 28 } 29 assert_eq!( 30 Opt { arg: 42 }, 31 Opt::try_parse_from(["test", "-a42"]).unwrap() 32 ); 33 assert_eq!( 34 Opt { arg: 42 }, 35 Opt::try_parse_from(["test", "-a", "42"]).unwrap() 36 ); 37 assert_eq!( 38 Opt { arg: 42 }, 39 Opt::try_parse_from(["test", "--arg", "42"]).unwrap() 40 ); 41 assert_eq!( 42 Opt { arg: 42 }, 43 Opt::try_parse_from(["test", "--arg", "24", "--arg", "42"]).unwrap() 44 ); 45 assert!(Opt::try_parse_from(["test"]).is_err()); 46} 47 48#[test] 49fn option_with_default() { 50 #[derive(Parser, PartialEq, Debug)] 51 #[command(args_override_self = true)] 52 struct Opt { 53 #[arg(short, default_value = "42")] 54 arg: i32, 55 } 56 assert_eq!( 57 Opt { arg: 24 }, 58 Opt::try_parse_from(["test", "-a24"]).unwrap() 59 ); 60 assert_eq!( 61 Opt { arg: 42 }, 62 Opt::try_parse_from(["test", "-a", "24", "-a", "42"]).unwrap() 63 ); 64 assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(["test"]).unwrap()); 65} 66 67#[test] 68fn option_with_raw_default() { 69 #[derive(Parser, PartialEq, Debug)] 70 #[command(args_override_self = true)] 71 struct Opt { 72 #[arg(short, default_value = "42")] 73 arg: i32, 74 } 75 assert_eq!( 76 Opt { arg: 24 }, 77 Opt::try_parse_from(["test", "-a24"]).unwrap() 78 ); 79 assert_eq!( 80 Opt { arg: 42 }, 81 Opt::try_parse_from(["test", "-a", "24", "-a", "42"]).unwrap() 82 ); 83 assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(["test"]).unwrap()); 84} 85 86#[test] 87fn option_from_str() { 88 #[derive(Clone, Debug, PartialEq)] 89 struct A; 90 91 impl std::str::FromStr for A { 92 type Err = std::convert::Infallible; 93 94 fn from_str(_: &str) -> Result<A, Self::Err> { 95 Ok(A) 96 } 97 } 98 99 #[derive(Debug, Parser, PartialEq)] 100 #[command(args_override_self = true)] 101 struct Opt { 102 a: Option<A>, 103 } 104 105 assert_eq!(Opt { a: None }, Opt::try_parse_from(["test"]).unwrap()); 106 assert_eq!( 107 Opt { a: Some(A) }, 108 Opt::try_parse_from(["test", "foo"]).unwrap() 109 ); 110} 111 112#[test] 113fn vec_from_str() { 114 #[derive(Clone, Debug, PartialEq)] 115 struct A; 116 117 impl std::str::FromStr for A { 118 type Err = std::convert::Infallible; 119 120 fn from_str(_: &str) -> Result<A, Self::Err> { 121 Ok(A) 122 } 123 } 124 125 #[derive(Debug, Parser, PartialEq)] 126 #[command(args_override_self = true)] 127 struct Opt { 128 a: Vec<A>, 129 } 130 131 assert_eq!( 132 Opt { a: Vec::new() }, 133 Opt::try_parse_from(["test"]).unwrap() 134 ); 135 assert_eq!( 136 Opt { a: vec![A] }, 137 Opt::try_parse_from(["test", "foo"]).unwrap() 138 ); 139} 140 141#[test] 142fn option_vec_from_str() { 143 #[derive(Clone, Debug, PartialEq)] 144 struct A; 145 146 impl std::str::FromStr for A { 147 type Err = std::convert::Infallible; 148 149 fn from_str(_: &str) -> Result<A, Self::Err> { 150 Ok(A) 151 } 152 } 153 154 #[derive(Debug, Parser, PartialEq)] 155 #[command(args_override_self = true)] 156 struct Opt { 157 #[arg(short)] 158 a: Option<Vec<A>>, 159 } 160 161 assert_eq!(Opt { a: None }, Opt::try_parse_from(["test"]).unwrap()); 162 assert_eq!( 163 Opt { a: Some(vec![A]) }, 164 Opt::try_parse_from(["test", "-a", "foo"]).unwrap() 165 ); 166} 167 168#[test] 169fn option_type_is_optional() { 170 #[derive(Parser, PartialEq, Debug)] 171 #[command(args_override_self = true)] 172 struct Opt { 173 #[arg(short)] 174 arg: Option<i32>, 175 } 176 assert_eq!( 177 Opt { arg: Some(42) }, 178 Opt::try_parse_from(["test", "-a42"]).unwrap() 179 ); 180 assert_eq!( 181 Opt { arg: Some(42) }, 182 Opt::try_parse_from(["test", "-a", "24", "-a", "42"]).unwrap() 183 ); 184 assert_eq!(Opt { arg: None }, Opt::try_parse_from(["test"]).unwrap()); 185} 186 187#[test] 188fn required_with_option_type() { 189 #[derive(Debug, PartialEq, Eq, Parser)] 190 #[command(subcommand_negates_reqs = true)] 191 #[command(args_override_self = true)] 192 struct Opt { 193 #[arg(required = true)] 194 req_str: Option<String>, 195 196 #[command(subcommand)] 197 cmd: Option<SubCommands>, 198 } 199 200 #[derive(Debug, PartialEq, Eq, Subcommand)] 201 enum SubCommands { 202 ExSub { 203 #[arg(short, long, action = clap::ArgAction::Count)] 204 verbose: u8, 205 }, 206 } 207 208 assert_eq!( 209 Opt { 210 req_str: Some(("arg").into()), 211 cmd: None, 212 }, 213 Opt::try_parse_from(["test", "arg"]).unwrap() 214 ); 215 216 assert_eq!( 217 Opt { 218 req_str: None, 219 cmd: Some(SubCommands::ExSub { verbose: 1 }), 220 }, 221 Opt::try_parse_from(["test", "ex-sub", "-v"]).unwrap() 222 ); 223 224 assert!(Opt::try_parse_from(["test"]).is_err()); 225} 226 227#[test] 228fn ignore_qualified_option_type() { 229 fn parser(s: &str) -> Result<Option<String>, std::convert::Infallible> { 230 Ok(Some(s.to_string())) 231 } 232 233 #[derive(Parser, PartialEq, Debug)] 234 #[command(args_override_self = true)] 235 struct Opt { 236 #[arg(value_parser = parser)] 237 arg: ::std::option::Option<String>, 238 } 239 240 assert_eq!( 241 Opt { 242 arg: Some("success".into()) 243 }, 244 Opt::try_parse_from(["test", "success"]).unwrap() 245 ); 246} 247 248#[test] 249fn option_option_type_is_optional_value() { 250 #[derive(Parser, PartialEq, Debug)] 251 #[command(args_override_self = true)] 252 struct Opt { 253 #[arg(short)] 254 #[allow(clippy::option_option)] 255 arg: Option<Option<i32>>, 256 } 257 assert_eq!( 258 Opt { 259 arg: Some(Some(42)) 260 }, 261 Opt::try_parse_from(["test", "-a42"]).unwrap() 262 ); 263 assert_eq!( 264 Opt { arg: Some(None) }, 265 Opt::try_parse_from(["test", "-a"]).unwrap() 266 ); 267 assert_eq!( 268 Opt { 269 arg: Some(Some(42)) 270 }, 271 Opt::try_parse_from(["test", "-a", "24", "-a", "42"]).unwrap() 272 ); 273 assert_eq!(Opt { arg: None }, Opt::try_parse_from(["test"]).unwrap()); 274} 275 276#[test] 277fn option_option_type_help() { 278 #[derive(Parser, Debug)] 279 #[command(args_override_self = true)] 280 struct Opt { 281 #[arg(long, value_name = "val")] 282 arg: Option<Option<i32>>, 283 } 284 let help = utils::get_help::<Opt>(); 285 assert!(help.contains("--arg [<val>]")); 286 assert!(!help.contains("--arg [<val>...]")); 287} 288 289#[test] 290fn two_option_option_types() { 291 #[derive(Parser, PartialEq, Debug)] 292 #[command(args_override_self = true)] 293 struct Opt { 294 #[arg(short)] 295 arg: Option<Option<i32>>, 296 297 #[arg(long)] 298 field: Option<Option<String>>, 299 } 300 assert_eq!( 301 Opt { 302 arg: Some(Some(42)), 303 field: Some(Some("f".into())) 304 }, 305 Opt::try_parse_from(["test", "-a42", "--field", "f"]).unwrap() 306 ); 307 assert_eq!( 308 Opt { 309 arg: Some(Some(42)), 310 field: Some(None) 311 }, 312 Opt::try_parse_from(["test", "-a42", "--field"]).unwrap() 313 ); 314 assert_eq!( 315 Opt { 316 arg: Some(None), 317 field: Some(None) 318 }, 319 Opt::try_parse_from(["test", "-a", "--field"]).unwrap() 320 ); 321 assert_eq!( 322 Opt { 323 arg: Some(None), 324 field: Some(Some("f".into())) 325 }, 326 Opt::try_parse_from(["test", "-a", "--field", "f"]).unwrap() 327 ); 328 assert_eq!( 329 Opt { 330 arg: None, 331 field: Some(None) 332 }, 333 Opt::try_parse_from(["test", "--field"]).unwrap() 334 ); 335 assert_eq!( 336 Opt { 337 arg: None, 338 field: None 339 }, 340 Opt::try_parse_from(["test"]).unwrap() 341 ); 342} 343 344#[test] 345fn vec_type_is_multiple_occurrences() { 346 #[derive(Parser, PartialEq, Debug)] 347 #[command(args_override_self = true)] 348 struct Opt { 349 #[arg(short, long)] 350 arg: Vec<i32>, 351 } 352 assert_eq!( 353 Opt { arg: vec![24] }, 354 Opt::try_parse_from(["test", "-a24"]).unwrap() 355 ); 356 assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(["test"]).unwrap()); 357 assert_eq!( 358 Opt { arg: vec![24, 42] }, 359 Opt::try_parse_from(["test", "-a", "24", "-a", "42"]).unwrap() 360 ); 361} 362 363#[test] 364fn vec_type_with_required() { 365 #[derive(Parser, PartialEq, Debug)] 366 #[command(args_override_self = true)] 367 struct Opt { 368 #[arg(short, long, required = true)] 369 arg: Vec<i32>, 370 } 371 assert_eq!( 372 Opt { arg: vec![24] }, 373 Opt::try_parse_from(["test", "-a24"]).unwrap() 374 ); 375 assert!(Opt::try_parse_from(["test"]).is_err()); 376 assert_eq!( 377 Opt { arg: vec![24, 42] }, 378 Opt::try_parse_from(["test", "-a", "24", "-a", "42"]).unwrap() 379 ); 380} 381 382#[test] 383fn vec_type_with_multiple_values_only() { 384 #[derive(Parser, PartialEq, Debug)] 385 #[command(args_override_self = true)] 386 struct Opt { 387 #[arg(short, long, num_args(1..))] 388 arg: Vec<i32>, 389 } 390 assert_eq!( 391 Opt { arg: vec![24] }, 392 Opt::try_parse_from(["test", "-a24"]).unwrap() 393 ); 394 assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(["test"]).unwrap()); 395 assert_eq!( 396 Opt { arg: vec![24, 42] }, 397 Opt::try_parse_from(["test", "-a", "24", "42"]).unwrap() 398 ); 399} 400 401#[test] 402fn ignore_qualified_vec_type() { 403 fn parser(s: &str) -> Result<Vec<String>, std::convert::Infallible> { 404 Ok(vec![s.to_string()]) 405 } 406 407 #[derive(Parser, PartialEq, Debug)] 408 #[command(args_override_self = true)] 409 struct Opt { 410 #[arg(value_parser = parser)] 411 arg: ::std::vec::Vec<String>, 412 } 413 414 assert_eq!( 415 Opt { 416 arg: vec!["success".into()] 417 }, 418 Opt::try_parse_from(["test", "success"]).unwrap() 419 ); 420} 421 422#[test] 423fn option_vec_type() { 424 #[derive(Parser, PartialEq, Debug)] 425 #[command(args_override_self = true)] 426 struct Opt { 427 #[arg(short)] 428 arg: Option<Vec<i32>>, 429 } 430 assert_eq!( 431 Opt { arg: Some(vec![1]) }, 432 Opt::try_parse_from(["test", "-a", "1"]).unwrap() 433 ); 434 435 assert_eq!( 436 Opt { 437 arg: Some(vec![1, 2]) 438 }, 439 Opt::try_parse_from(["test", "-a", "1", "-a", "2"]).unwrap() 440 ); 441 442 assert_eq!(Opt { arg: None }, Opt::try_parse_from(["test"]).unwrap()); 443} 444 445#[test] 446fn option_vec_type_structopt_behavior() { 447 #[derive(Parser, PartialEq, Debug)] 448 #[command(args_override_self = true)] 449 struct Opt { 450 #[arg(short, long, num_args(0..))] 451 arg: Option<Vec<i32>>, 452 } 453 assert_eq!( 454 Opt { arg: Some(vec![1]) }, 455 Opt::try_parse_from(["test", "-a", "1"]).unwrap() 456 ); 457 458 assert_eq!( 459 Opt { 460 arg: Some(vec![1, 2]) 461 }, 462 Opt::try_parse_from(["test", "-a", "1", "2"]).unwrap() 463 ); 464 465 assert_eq!( 466 Opt { arg: Some(vec![]) }, 467 Opt::try_parse_from(["test", "-a"]).unwrap() 468 ); 469 470 assert_eq!(Opt { arg: None }, Opt::try_parse_from(["test"]).unwrap()); 471} 472 473#[test] 474fn two_option_vec_types() { 475 #[derive(Parser, PartialEq, Debug)] 476 #[command(args_override_self = true)] 477 struct Opt { 478 #[arg(short)] 479 arg: Option<Vec<i32>>, 480 481 #[arg(short)] 482 b: Option<Vec<i32>>, 483 } 484 485 assert_eq!( 486 Opt { 487 arg: Some(vec![1]), 488 b: None, 489 }, 490 Opt::try_parse_from(["test", "-a", "1"]).unwrap() 491 ); 492 493 assert_eq!( 494 Opt { 495 arg: Some(vec![1]), 496 b: Some(vec![1]) 497 }, 498 Opt::try_parse_from(["test", "-a", "1", "-b", "1"]).unwrap() 499 ); 500 501 assert_eq!( 502 Opt { 503 arg: Some(vec![1, 2]), 504 b: Some(vec![1, 2]) 505 }, 506 Opt::try_parse_from(["test", "-a", "1", "-a", "2", "-b", "1", "-b", "2"]).unwrap() 507 ); 508 509 assert_eq!( 510 Opt { arg: None, b: None }, 511 Opt::try_parse_from(["test"]).unwrap() 512 ); 513} 514 515#[test] 516fn explicit_value_parser() { 517 #[derive(Parser, PartialEq, Debug)] 518 #[command(args_override_self = true)] 519 struct Opt { 520 #[arg(long, value_parser = clap::value_parser!(i32))] 521 arg: i32, 522 } 523 assert_eq!( 524 Opt { arg: 42 }, 525 Opt::try_parse_from(["test", "--arg", "42"]).unwrap() 526 ); 527} 528 529#[test] 530fn implicit_value_parser() { 531 #[derive(Parser, PartialEq, Debug)] 532 #[command(args_override_self = true)] 533 struct Opt { 534 #[arg(long)] 535 arg: i32, 536 } 537 assert_eq!( 538 Opt { arg: 42 }, 539 Opt::try_parse_from(["test", "--arg", "42"]).unwrap() 540 ); 541} 542