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 15use clap::builder::BoolishValueParser; 16use clap::builder::TypedValueParser as _; 17use clap::ArgAction; 18use clap::CommandFactory; 19use clap::Parser; 20 21#[test] 22fn bool_type_is_flag() { 23 #[derive(Parser, PartialEq, Eq, Debug)] 24 #[command(args_override_self = true)] 25 struct Opt { 26 #[arg(short, long)] 27 alice: bool, 28 } 29 30 assert_eq!(Opt { alice: false }, Opt::try_parse_from(["test"]).unwrap()); 31 assert_eq!( 32 Opt { alice: true }, 33 Opt::try_parse_from(["test", "-a"]).unwrap() 34 ); 35 assert_eq!( 36 Opt { alice: true }, 37 Opt::try_parse_from(["test", "-a", "-a"]).unwrap() 38 ); 39 assert_eq!( 40 Opt { alice: true }, 41 Opt::try_parse_from(["test", "--alice"]).unwrap() 42 ); 43 assert!(Opt::try_parse_from(["test", "-i"]).is_err()); 44 assert!(Opt::try_parse_from(["test", "-a", "foo"]).is_err()); 45} 46 47#[test] 48fn non_bool_type_flag() { 49 fn parse_from_flag(b: bool) -> usize { 50 if b { 51 10 52 } else { 53 5 54 } 55 } 56 57 #[derive(Parser, Debug)] 58 struct Opt { 59 #[arg(short, long, action = ArgAction::SetTrue, value_parser = BoolishValueParser::new().map(parse_from_flag))] 60 alice: usize, 61 #[arg(short, long, action = ArgAction::SetTrue, value_parser = BoolishValueParser::new().map(parse_from_flag))] 62 bob: usize, 63 } 64 65 let opt = Opt::try_parse_from(["test"]).unwrap(); 66 assert_eq!(opt.alice, 5); 67 assert_eq!(opt.bob, 5); 68 69 let opt = Opt::try_parse_from(["test", "-a"]).unwrap(); 70 assert_eq!(opt.alice, 10); 71 assert_eq!(opt.bob, 5); 72 73 let opt = Opt::try_parse_from(["test", "-b"]).unwrap(); 74 assert_eq!(opt.alice, 5); 75 assert_eq!(opt.bob, 10); 76 77 let opt = Opt::try_parse_from(["test", "-b", "-a"]).unwrap(); 78 assert_eq!(opt.alice, 10); 79 assert_eq!(opt.bob, 10); 80} 81 82#[test] 83#[ignore] // Not a good path for supporting this atm 84fn inferred_help() { 85 #[derive(Parser, PartialEq, Eq, Debug)] 86 struct Opt { 87 /// Foo 88 #[arg(short, long)] 89 help: bool, 90 } 91 92 let mut cmd = Opt::command(); 93 cmd.build(); 94 let arg = cmd.get_arguments().find(|a| a.get_id() == "help").unwrap(); 95 assert_eq!( 96 arg.get_help().map(|s| s.to_string()), 97 Some("Foo".to_owned()), 98 "Incorrect help" 99 ); 100 assert!(matches!(arg.get_action(), clap::ArgAction::Help)); 101} 102 103#[test] 104#[ignore] // Not a good path for supporting this atm 105fn inferred_version() { 106 #[derive(Parser, PartialEq, Eq, Debug)] 107 struct Opt { 108 /// Foo 109 #[arg(short, long)] 110 version: bool, 111 } 112 113 let mut cmd = Opt::command(); 114 cmd.build(); 115 let arg = cmd 116 .get_arguments() 117 .find(|a| a.get_id() == "version") 118 .unwrap(); 119 assert_eq!( 120 arg.get_help().map(|s| s.to_string()), 121 Some("Foo".to_owned()), 122 "Incorrect help" 123 ); 124 assert!(matches!(arg.get_action(), clap::ArgAction::Version)); 125} 126 127#[test] 128fn count() { 129 #[derive(Parser, PartialEq, Eq, Debug)] 130 struct Opt { 131 #[arg(short, long, action = clap::ArgAction::Count)] 132 alice: u8, 133 #[arg(short, long, action = clap::ArgAction::Count)] 134 bob: u8, 135 } 136 137 assert_eq!( 138 Opt { alice: 0, bob: 0 }, 139 Opt::try_parse_from(["test"]).unwrap() 140 ); 141 assert_eq!( 142 Opt { alice: 1, bob: 0 }, 143 Opt::try_parse_from(["test", "-a"]).unwrap() 144 ); 145 assert_eq!( 146 Opt { alice: 2, bob: 0 }, 147 Opt::try_parse_from(["test", "-a", "-a"]).unwrap() 148 ); 149 assert_eq!( 150 Opt { alice: 2, bob: 2 }, 151 Opt::try_parse_from(["test", "-a", "--alice", "-bb"]).unwrap() 152 ); 153 assert_eq!( 154 Opt { alice: 3, bob: 1 }, 155 Opt::try_parse_from(["test", "-aaa", "--bob"]).unwrap() 156 ); 157 assert!(Opt::try_parse_from(["test", "-i"]).is_err()); 158 assert!(Opt::try_parse_from(["test", "-a", "foo"]).is_err()); 159} 160 161#[test] 162fn mixed_type_flags() { 163 #[derive(Parser, PartialEq, Eq, Debug)] 164 struct Opt { 165 #[arg(short, long)] 166 alice: bool, 167 #[arg(short, long, action = clap::ArgAction::Count)] 168 bob: u8, 169 } 170 171 assert_eq!( 172 Opt { 173 alice: false, 174 bob: 0 175 }, 176 Opt::try_parse_from(["test"]).unwrap() 177 ); 178 assert_eq!( 179 Opt { 180 alice: true, 181 bob: 0 182 }, 183 Opt::try_parse_from(["test", "-a"]).unwrap() 184 ); 185 assert_eq!( 186 Opt { 187 alice: true, 188 bob: 0 189 }, 190 Opt::try_parse_from(["test", "-a"]).unwrap() 191 ); 192 assert_eq!( 193 Opt { 194 alice: false, 195 bob: 1 196 }, 197 Opt::try_parse_from(["test", "-b"]).unwrap() 198 ); 199 assert_eq!( 200 Opt { 201 alice: true, 202 bob: 1 203 }, 204 Opt::try_parse_from(["test", "--alice", "--bob"]).unwrap() 205 ); 206 assert_eq!( 207 Opt { 208 alice: true, 209 bob: 4 210 }, 211 Opt::try_parse_from(["test", "-bb", "-a", "-bb"]).unwrap() 212 ); 213} 214 215#[test] 216fn ignore_qualified_bool_type() { 217 mod inner { 218 #[allow(non_camel_case_types)] 219 #[derive(PartialEq, Eq, Debug, Clone)] 220 pub struct bool(pub String); 221 222 impl std::str::FromStr for self::bool { 223 type Err = String; 224 225 fn from_str(s: &str) -> Result<Self, Self::Err> { 226 Ok(self::bool(s.into())) 227 } 228 } 229 } 230 231 #[derive(Parser, PartialEq, Eq, Debug)] 232 struct Opt { 233 arg: inner::bool, 234 } 235 236 assert_eq!( 237 Opt { 238 arg: inner::bool("success".into()) 239 }, 240 Opt::try_parse_from(["test", "success"]).unwrap() 241 ); 242} 243 244#[test] 245fn override_implicit_action() { 246 #[derive(Parser, PartialEq, Eq, Debug)] 247 struct Opt { 248 #[arg(long, action = clap::ArgAction::Set)] 249 arg: bool, 250 } 251 252 assert_eq!( 253 Opt { arg: false }, 254 Opt::try_parse_from(["test", "--arg", "false"]).unwrap() 255 ); 256 257 assert_eq!( 258 Opt { arg: true }, 259 Opt::try_parse_from(["test", "--arg", "true"]).unwrap() 260 ); 261} 262 263#[test] 264fn override_implicit_from_flag_positional() { 265 #[derive(Parser, PartialEq, Eq, Debug)] 266 struct Opt { 267 #[arg(action = clap::ArgAction::Set)] 268 arg: bool, 269 } 270 271 assert_eq!( 272 Opt { arg: false }, 273 Opt::try_parse_from(["test", "false"]).unwrap() 274 ); 275 276 assert_eq!( 277 Opt { arg: true }, 278 Opt::try_parse_from(["test", "true"]).unwrap() 279 ); 280} 281 282#[test] 283fn unit_for_negation() { 284 #[derive(Parser, PartialEq, Eq, Debug)] 285 struct Opt { 286 #[arg(long)] 287 arg: bool, 288 #[arg(long, action = ArgAction::SetTrue, overrides_with = "arg")] 289 no_arg: (), 290 } 291 292 assert_eq!( 293 Opt { 294 arg: false, 295 no_arg: () 296 }, 297 Opt::try_parse_from(["test"]).unwrap() 298 ); 299 300 assert_eq!( 301 Opt { 302 arg: true, 303 no_arg: () 304 }, 305 Opt::try_parse_from(["test", "--arg"]).unwrap() 306 ); 307 308 assert_eq!( 309 Opt { 310 arg: false, 311 no_arg: () 312 }, 313 Opt::try_parse_from(["test", "--no-arg"]).unwrap() 314 ); 315 316 assert_eq!( 317 Opt { 318 arg: true, 319 no_arg: () 320 }, 321 Opt::try_parse_from(["test", "--no-arg", "--arg"]).unwrap() 322 ); 323 324 assert_eq!( 325 Opt { 326 arg: false, 327 no_arg: () 328 }, 329 Opt::try_parse_from(["test", "--arg", "--no-arg"]).unwrap() 330 ); 331} 332