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 crate::utils; 16 17use clap::{Args, Parser, Subcommand}; 18 19#[test] 20fn flatten() { 21 #[derive(Args, PartialEq, Debug)] 22 struct Common { 23 arg: i32, 24 } 25 26 #[derive(Parser, PartialEq, Debug)] 27 struct Opt { 28 #[command(flatten)] 29 common: Common, 30 } 31 assert_eq!( 32 Opt { 33 common: Common { arg: 42 } 34 }, 35 Opt::try_parse_from(["test", "42"]).unwrap() 36 ); 37 assert!(Opt::try_parse_from(["test"]).is_err()); 38 assert!(Opt::try_parse_from(["test", "42", "24"]).is_err()); 39} 40 41#[cfg(debug_assertions)] 42#[test] 43#[should_panic] 44fn flatten_twice() { 45 #[derive(Args, PartialEq, Debug)] 46 struct Common { 47 arg: i32, 48 } 49 50 #[derive(Parser, PartialEq, Debug)] 51 struct Opt { 52 #[command(flatten)] 53 c1: Common, 54 // Defines "arg" twice, so this should not work. 55 #[command(flatten)] 56 c2: Common, 57 } 58 Opt::try_parse_from(["test", "42", "43"]).unwrap(); 59} 60 61#[test] 62fn flatten_in_subcommand() { 63 #[derive(Args, PartialEq, Debug)] 64 struct Common { 65 arg: i32, 66 } 67 68 #[derive(Args, PartialEq, Debug)] 69 struct Add { 70 #[arg(short)] 71 interactive: bool, 72 #[command(flatten)] 73 common: Common, 74 } 75 76 #[derive(Parser, PartialEq, Debug)] 77 enum Opt { 78 Fetch { 79 #[arg(short)] 80 all: bool, 81 #[command(flatten)] 82 common: Common, 83 }, 84 85 Add(Add), 86 } 87 88 assert_eq!( 89 Opt::Fetch { 90 all: false, 91 common: Common { arg: 42 } 92 }, 93 Opt::try_parse_from(["test", "fetch", "42"]).unwrap() 94 ); 95 assert_eq!( 96 Opt::Add(Add { 97 interactive: true, 98 common: Common { arg: 43 } 99 }), 100 Opt::try_parse_from(["test", "add", "-i", "43"]).unwrap() 101 ); 102} 103 104#[test] 105fn update_args_with_flatten() { 106 #[derive(Args, PartialEq, Debug)] 107 struct Common { 108 arg: i32, 109 } 110 111 #[derive(Parser, PartialEq, Debug)] 112 struct Opt { 113 #[command(flatten)] 114 common: Common, 115 } 116 117 let mut opt = Opt { 118 common: Common { arg: 42 }, 119 }; 120 opt.try_update_from(["test"]).unwrap(); 121 assert_eq!(Opt::try_parse_from(["test", "42"]).unwrap(), opt); 122 123 let mut opt = Opt { 124 common: Common { arg: 42 }, 125 }; 126 opt.try_update_from(["test", "52"]).unwrap(); 127 assert_eq!(Opt::try_parse_from(["test", "52"]).unwrap(), opt); 128} 129 130#[derive(Subcommand, PartialEq, Debug)] 131enum BaseCli { 132 Command1(Command1), 133} 134 135#[derive(Args, PartialEq, Debug)] 136struct Command1 { 137 arg1: i32, 138 139 arg2: i32, 140} 141 142#[derive(Args, PartialEq, Debug)] 143struct Command2 { 144 arg2: i32, 145} 146 147#[derive(Parser, PartialEq, Debug)] 148enum Opt { 149 #[command(flatten)] 150 BaseCli(BaseCli), 151 Command2(Command2), 152} 153 154#[test] 155fn merge_subcommands_with_flatten() { 156 assert_eq!( 157 Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 42, arg2: 44 })), 158 Opt::try_parse_from(["test", "command1", "42", "44"]).unwrap() 159 ); 160 assert_eq!( 161 Opt::Command2(Command2 { arg2: 43 }), 162 Opt::try_parse_from(["test", "command2", "43"]).unwrap() 163 ); 164} 165 166#[test] 167fn update_subcommands_with_flatten() { 168 let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 })); 169 opt.try_update_from(["test", "command1", "42", "44"]) 170 .unwrap(); 171 assert_eq!( 172 Opt::try_parse_from(["test", "command1", "42", "44"]).unwrap(), 173 opt 174 ); 175 176 let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 })); 177 opt.try_update_from(["test", "command1", "42"]).unwrap(); 178 assert_eq!( 179 Opt::try_parse_from(["test", "command1", "42", "14"]).unwrap(), 180 opt 181 ); 182 183 let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 })); 184 opt.try_update_from(["test", "command2", "43"]).unwrap(); 185 assert_eq!( 186 Opt::try_parse_from(["test", "command2", "43"]).unwrap(), 187 opt 188 ); 189} 190 191#[test] 192fn flatten_with_doc_comment() { 193 #[derive(Args, PartialEq, Debug)] 194 struct Common { 195 /// This is an arg. Arg means "argument". Command line argument. 196 arg: i32, 197 } 198 199 #[derive(Parser, PartialEq, Debug)] 200 struct Opt { 201 /// The very important comment that clippy had me put here. 202 /// It knows better. 203 #[command(flatten)] 204 common: Common, 205 } 206 assert_eq!( 207 Opt { 208 common: Common { arg: 42 } 209 }, 210 Opt::try_parse_from(["test", "42"]).unwrap() 211 ); 212 213 let help = utils::get_help::<Opt>(); 214 assert!(help.contains("This is an arg.")); 215 assert!(!help.contains("The very important")); 216} 217 218#[test] 219fn docstrings_ordering_with_multiple_command() { 220 /// This is the docstring for Flattened 221 #[derive(Args)] 222 struct Flattened { 223 #[arg(long)] 224 foo: bool, 225 } 226 227 /// This is the docstring for Command 228 #[derive(Parser)] 229 struct Command { 230 #[command(flatten)] 231 flattened: Flattened, 232 } 233 234 let short_help = utils::get_help::<Command>(); 235 236 assert!(short_help.contains("This is the docstring for Command")); 237} 238 239#[test] 240fn docstrings_ordering_with_multiple_clap_partial() { 241 /// This is the docstring for Flattened 242 #[derive(Args)] 243 struct Flattened { 244 #[arg(long)] 245 foo: bool, 246 } 247 248 #[derive(Parser)] 249 struct Command { 250 #[command(flatten)] 251 flattened: Flattened, 252 } 253 254 let short_help = utils::get_help::<Command>(); 255 256 assert!(short_help.contains("This is the docstring for Flattened")); 257} 258 259#[test] 260fn optional_flatten() { 261 #[derive(Parser, Debug, PartialEq, Eq)] 262 struct Opt { 263 #[command(flatten)] 264 source: Option<Source>, 265 } 266 267 #[derive(clap::Args, Debug, PartialEq, Eq)] 268 struct Source { 269 crates: Vec<String>, 270 #[arg(long)] 271 path: Option<std::path::PathBuf>, 272 #[arg(long)] 273 git: Option<String>, 274 } 275 276 assert_eq!(Opt { source: None }, Opt::try_parse_from(["test"]).unwrap()); 277 assert_eq!( 278 Opt { 279 source: Some(Source { 280 crates: vec!["serde".to_owned()], 281 path: None, 282 git: None, 283 }), 284 }, 285 Opt::try_parse_from(["test", "serde"]).unwrap() 286 ); 287 assert_eq!( 288 Opt { 289 source: Some(Source { 290 crates: Vec::new(), 291 path: Some("./".into()), 292 git: None, 293 }), 294 }, 295 Opt::try_parse_from(["test", "--path=./"]).unwrap() 296 ); 297} 298