{"id":21436,"date":"2024-03-04T22:45:53","date_gmt":"2024-03-05T03:45:53","guid":{"rendered":"https:\/\/qxf2.com\/blog\/?p=21436"},"modified":"2024-03-04T22:45:53","modified_gmt":"2024-03-05T03:45:53","slug":"rust-clap-journey-a-little-further","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/rust-clap-journey-a-little-further\/","title":{"rendered":"Rust Clap journey &#8211; a little further"},"content":{"rendered":"<p>At <a href=\"https:\/\/qxf2.com\/?utm_source=Clap_little_further&#038;utm_medium=click&#038;utm_campaign=From%20blog\" rel=\"noopener\" target=\"_blank\">Qxf2 <\/a>we are always on the lookout to go wider and deeper into evolving our technical toolbelt. One such initiative was to explore popular Rust crates and to come up with code samples showing implementation of important features. <a href=\"https:\/\/crates.io\/crates\/clap\" rel=\"noopener\" target=\"_blank\">Clap<\/a> is a popular crate in Rust, that aids in developing CLI applications seamlessly. In our opening <a href=\"https:\/\/qxf2.com\/blog\/brief-introduction-to-clap\/\" rel=\"noopener\" target=\"_blank\">blog<\/a>, we gave you a soft introduction into creating your first CLI app using Clap, arguments grouping and finally parsing them for further processing. Let us take the Rust Clap journey &#8211; a little further. In this blog, we will help you dive further into Clap, by showing you Clap&#8217;s implementation of optional arguments, subcommands, and more.<\/p>\n<hr>\n<h4>Configuring short and long versions of command-line arguments<\/h4>\n<p>First, let&#8217;s look at how we can configure the long and short versions of command line arguments.<br \/>\n<u><i>short_long_args.rs<\/i><\/u><\/p>\n<pre lang=\"Rust\">\r\nuse clap::Parser;\r\n\/\/\/Fetch login details\r\n#[derive(Parser, Debug)]\r\nstruct Args {\r\n    #[arg(short = 'u', long =\"username\")]\r\n    user_name: String,\r\n    #[arg(short = 'p', long = \"password\")]\r\n    password: String,\r\n}\r\nfn main() {\r\n    let args = Args::parse();\r\n    println!(\"{:?}\", args);\r\n}\r\n<\/pre>\n<p>To start with, we have imported Clap\u2019s Parser trait and then defined the command-line arguments using Clap\u2019s Args struct. <a href=\"https:\/\/docs.rs\/clap\/latest\/clap\" rel=\"noopener\" target=\"_blank\">Clap\u2019s documentation<\/a> neatly explains Args as an abstract representation of a command line argument. It is used to set all the options and relationships that define a valid argument for the program. <\/p>\n<h6>Breaking down the code:<\/h6>\n<p>We have used Args to define two command-line arguments namely username and password. <\/p>\n<pre lang=\"Rust\"> \r\n#[arg(short = 'u', long =\"username\")]<\/pre>\n<p>#[arg] is an attribute from Clap that is used to provides metadata about how to handle command-line arguments. short and long is used to specify short and long versions of the argument. So now when we call the binary, we can use either `-u` or `&#45;&#45;username` to pass value to username argument. By default, arguments defined are mandatory unless the default_value parameter is set, which we will discuss in a bit. <\/p>\n<pre lang=\"Rust\">\r\nlet args = Args::parse();\r\nprintln!(\"{:?}\", args);\r\n<\/pre>\n<p>Args::parse() is a method provided by the Parser trait to parse command-line arguments. We are then printing the Args struct fields with values to ensure they are parsed correctly.<\/p>\n<h6>Run code:<TBD><\/h6>\n<figure id=\"attachment_21388\" aria-describedby=\"caption-attachment-21388\" style=\"width: 900px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2024\/02\/short_long_args_run-1.gif\" data-rel=\"lightbox-image-0\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2024\/02\/short_long_args_run-1.gif\" alt=\"Executing short_long_args.rs\" width=\"900\" height=\"500\" class=\"size-large wp-image-21388\" \/><\/a><figcaption id=\"caption-attachment-21388\" class=\"wp-caption-text\">Executing short_long_args.rs<\/figcaption><\/figure>\n<hr>\n<h4>Defining mandatory and optional command-line arguments<\/h4>\n<p>Next, let&#8217;s focus on configuring mandatory and optional command-line arguments as they are vital for building any CLI app.<br \/>\n<u><i>optional_args.rs<\/i><\/u><\/p>\n<pre lang=\"Rust\">\r\nuse clap::Parser;\r\nuse std::env;\r\n\/\/\/Login validation\r\n#[derive(Parser, Debug)]\r\nstruct Args {\r\n    \/\/\/ User Name - optional argument\r\n    #[arg(short = 'u', long =\"username\", default_value = \"Qxf2\")]\r\n    user_name: String,\r\n    \/\/\/ Password - mandatory argument\r\n    #[arg(short = 'p', long = \"password\", required = true)]\r\n    password: String,\r\n}\r\nfn main() {\r\n    let args = Args::parse();\r\n    let expected_password = match env::var(\"APP_PASSWORD\") {\r\n        Ok(val) => val,\r\n        Err(_) => {\r\n            println!(\"Password not set as an environment variable!\");\r\n            return;\r\n        }\r\n    };\r\n    if args.password == expected_password{\r\n        println!(\"Welcome to {}!\", args.user_name);\r\n    }\r\n    else {\r\n        println!(\"Password mismatch for user: {}! \\nLogin failed!\", args.user_name);\r\n    }\r\n}\r\n<\/pre>\n<h6>Breaking down the code:<\/h6>\n<p>We have extended the previous example. This time we&#8217;ve also defined mandatory and optional arguments and performed validation. &#8220;\/\/\/&#8221; marks the start of documentation comments. Documentation comments make help feature of clap more customizable as well as improves code readability. <\/p>\n<pre lang=\"Rust\">\r\n#[arg(short = 'u', long =\"username\", default_value = \"Qxf2\")]\r\n<\/pre>\n<p>Setting default_value parameter in #[arg] attribute makes a command-line argument optional. Argument &#8220;username&#8221; is an optional argument as it takes a default value. Whereas in the following code,<\/p>\n<pre lang=\"Rust\">\r\n#[arg(short = 'p', long = \"password\", required = true)]\r\n<\/pre>\n<p>Argument &#8220;password&#8221; is a mandatory argument to be passed from the command-line. <\/p>\n<pre lang=\"Rust\">\r\nlet expected_password = match env::var(\"APP_PASSWORD\") {\r\n       Ok(val) => val,\r\n       Err(_) => {\r\n           println!(\"Password not set as an environment variable!\");\r\n           return;\r\n       }\r\n<\/pre>\n<p>std::env is a module in Rust&#8217;s standard library (std) that has functions and types used to work with the environment variables. We are initializing string variable &#8220;expected_password&#8221; with the value defined in the environment variable &#8220;APP_PASSWORD&#8221;. If we have set &#8220;APP_PASSWORD&#8221; then &#8220;expected_password&#8221; will be initialized with that value. If not, it will be handled in the error block.<\/p>\n<pre lang=\"Rust\">\r\nif args.password == expected_password{\r\n        println!(\"Welcome to {}!\", args.user_name);\r\n    }\r\n    else {\r\n        println!(\"Password mismatch for user: {}! \\nLogin failed!\", args.user_name);\r\n    }\r\n<\/pre>\n<p>We are then comparing password passed from command-line argument with that set in the environment variable and informing user of the validation outcome accordingly.   <\/p>\n<h6>Run command and expected outcome:<\/h6>\n<p>Let us explore the help for this program:<\/p>\n<pre lang=\"Shell\">\r\n$ cargo run --bin optional_args -- --help\r\nLogin validation\r\n\r\nUsage: optional_args.exe [OPTIONS] --password <PASSWORD>\r\n\r\nOptions:\r\n  -u, --username <USER_NAME>  User Name - optional argument [default: Qxf2]\r\n  -p, --password <PASSWORD>   Password - mandatory argument\r\n  -h, --help                  Print help\r\n<\/pre>\n<p>If we run this login validation code without setting environment variable &#8220;APP_PASSWORD&#8221;:<\/p>\n<pre lang=\"Shell\">\r\n$ cargo run --bin optional_args -- --password 123\r\nPassword not set as an environment variable!\r\n<\/pre>\n<p>Let us set the environment variable &#8220;APP_PASSWORD&#8221;,<\/p>\n<pre lang=\"Shell\">\r\nexport APP_PASSWORD=TestersAreSharp#$2024\r\n<\/pre>\n<p>and run the login validation code:<\/p>\n<pre lang=\"Shell\">\r\n$ cargo run --bin optional_args -- --password 123\r\nPassword mismatch for user: Qxf2!\r\nLogin failed!\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin optional_args -- --password TestersAreSharp#$2024\r\nWelcome to Qxf2!\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin optional_args -- -u Qxf2 -p TestersAreSharp#$2024\r\nWelcome to Qxf2!\r\n<\/pre>\n<hr>\n<h4>Defining positional argument and boolean options<\/h4>\n<p>As we are aware, positional argument is a type of command-line argument that is identified based on its position after the command. For example, in the copy command `cp source.txt destination.txt`, source.txt and destination.txt are positional arguments indexed at position 1 and 2 respectively. Order is important for positional arguments and they do not require any flag or keyword. In the case of boolean options(otherwise called as flags), their presence would simply mean true. If not mentioned on the command line, it would mean they are ignored for that run. As flags, they are used for turning on or off certain features in a program. Let us see an example to implement positional arguments and boolean options.<br \/>\n<u><i>positional_args_boolean_options.rs<\/i><\/u><\/p>\n<pre lang=\"Rust\">\r\nuse clap::{Command, Arg};\r\nfn main(){\r\n    \/\/ Build the CLI app using builder pattern\r\n    let cmd = Command::new(\"NumberProgram\")\r\n    .version(\"1.0\")\r\n    .about(\"Program to show usage of positional argument and boolean option using clap\")\r\n    .arg(\r\n        Arg::new(\"input_number\")\r\n        .help(\"a number\")\r\n        .index(1)\r\n        .value_parser(clap::value_parser!(i32))\r\n        .required(true)\r\n    )\r\n    .arg(\r\n        Arg::new(\"square\")\r\n        .help(\"Boolean option to get the square of the input number\")\r\n        .short('s')\r\n        .long(\"square\")\r\n        .required(false)\r\n        .num_args(0)\r\n    );\r\n    \/\/ Runtime argument parsing\r\n    let matches = cmd.get_matches();\r\n    let input_number = matches.get_one(\"input_number\");\r\n    \/\/Extract the value passed for the positional argument and display\r\n    let number = match input_number {\r\n        Some(input_number) => input_number,\r\n        None => &0,\r\n    };\r\n    println!(\"Number passed via CLI : {} \", number);\r\n    \/\/If boolean option is passed then print square\r\n    if let Some(true) = matches.get_one(\"square\"){\r\n        println!(\"Square of {} : {} \", number, number*number);\r\n    }\r\n}\r\n<\/pre>\n<h6>Breaking down code:<\/h6>\n<p>We have constructed a command &#8220;NumberProgram&#8221; that takes two arguments, using the builder pattern of Clap.<\/p>\n<pre lang=\"Rust\">\r\n.arg(\r\n        Arg::new(\"input_number\")\r\n        .help(\"a number\")\r\n        .index(1)\r\n        .value_parser(clap::value_parser!(i32))\r\n        .required(true)\r\n    )\r\n<\/pre>\n<p>First argument &#8220;input_number&#8221; is a positional argument which should be passed at index 1, accepts integer type and is a mandatory argument.<\/p>\n<pre lang=\"Rust\">\r\n .arg(\r\n        Arg::new(\"square\")\r\n        .help(\"Boolean option to get the square of the input number\")\r\n        .short('s')\r\n        .long(\"square\")\r\n        .required(false)\r\n        .num_args(0)\r\n    )\r\n<\/pre>\n<p>Second argument &#8220;square&#8221; is a flag or boolean option and this has been specified by mentioning required attribute as false. Also, the short and long versions of specifying this argument has been defined. num_args(0) would mean that this argument does not take any value. Rest of the code is simple. The positional arguments if integer, is displayed and If boolean option &#8220;-s&#8221; or &#8220;&#8211;square&#8221; is passed, then square of the number passed is displayed as well. <\/p>\n<h6>Run command and expected outcome:<\/h6>\n<pre lang=\"Shell\">\r\n$ cargo run --bin positional_args_boolean_options -- --help\r\nProgram to show usage of positional argument and boolean option using clap\r\n\r\nUsage: positional_args_boolean_options.exe [OPTIONS] <input_number>\r\n\r\nArguments:\r\n  <input_number>  a number\r\n\r\nOptions:\r\n  -s, --square   Boolean option to get the square of the input number\r\n  -h, --help     Print help\r\n  -V, --version  Print version\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin positional_args_boolean_options -- 5\r\nNumber passed via CLI : 5\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin positional_args_boolean_options -- 5 --square\r\nNumber passed via CLI : 5\r\nSquare of 5 : 25\r\n<\/pre>\n<hr>\n<h4>Implementing subcommands<\/h4>\n<p>Subcommands are keywords that are used to invoke a new set of options. It is one of the most important features of a command-line parser. Let us now see how to implement subcommands using Clap crate. We will be demonstrating subcommands taking Git operations as an example.<br \/>\n<u><i>subcommands.rs<\/i><\/u><\/p>\n<pre lang=\"Rust\">\r\nuse clap::{Command, Arg};\r\nfn main() {\r\n    let app_m = Command::new(\"MyGitApp\")\r\n        .subcommand(Command::new(\"clone\").about(\"Clone a repository\").arg(\r\n            Arg::new(\"name\")\r\n                .help(\"repo name\")\r\n                .short('n')\r\n                .long(\"name\")\r\n                .num_args(1)\r\n        ))\r\n        .subcommand(Command::new(\"push\").about(\"Push changes to a repository\").arg(\r\n            Arg::new(\"branch\")\r\n                .help(\"branch name\")\r\n                .short('b')\r\n                .long(\"branch\")\r\n                .num_args(1)\r\n        ))\r\n        .subcommand(Command::new(\"commit\").about(\"Commit changes to a repository\").arg(\r\n            Arg::new(\"message\")\r\n                .help(\"commit message\")\r\n                .short('m')\r\n                .long(\"message\")\r\n                .num_args(1)\r\n        ))\r\n        .get_matches();\r\n\r\n    match app_m.subcommand() {\r\n        Some((\"clone\", sub_m)) => {\r\n            \/\/ Handle clone subcommand\r\n            if let Some(name) = sub_m.get_one::<String>(\"name\") {\r\n                println!(\"Cloning repository with name: {}\", name);\r\n            }\r\n            else {\r\n                println!(\"Please provide name of repository to clone\");\r\n            }\r\n        }\r\n        Some((\"push\", sub_m)) => {\r\n            \/\/ Handle push subcommand\r\n            if let Some(branch) = sub_m.get_one::<String>(\"branch\") {\r\n                println!(\"Pushing changes to branch: {}\", branch);\r\n            }\r\n            else {\r\n                println!(\"Please provide the branch to push\");\r\n            }\r\n        }\r\n        Some((\"commit\", sub_m)) => {\r\n            \/\/ Handle commit subcommand\r\n            if let Some(message) = sub_m.get_one::<String>(\"message\") {\r\n                println!(\"Committing changes with message: {}\", message);\r\n            }\r\n            else {\r\n                println!(\"Please provide a commit message\");\r\n            }\r\n        }\r\n        _ => {\r\n            \/\/ Either no subcommand or invalid subcommand\r\n            println!(\"Invalid subcommand or no subcommand provided\");\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<h6>Breaking down code:<\/h6>\n<p>We have constructed three subcommands for the main command &#8220;MyGitApp&#8221; using Clap&#8217;s builder pattern. The subcommands are &#8220;clone&#8221;, &#8220;push&#8221; and &#8220;commit&#8221; and they take one argument each. At any point in time, only one subcommand can be passed as an input argument from the command line. get_matches() method is used to parse the command-line arguments. <\/p>\n<pre lang=\"Rust\">\r\n match app_m.subcommand() {\r\n        Some((\"clone\", sub_m)) => {\r\n            \/\/ Handle clone subcommand\r\n            if let Some(name) = sub_m.get_one::<String>(\"name\") {\r\n                println!(\"Cloning repository with name: {}\", name);\r\n            }\r\n            else {\r\n                println!(\"Please provide name of repository to clone\");\r\n            }\r\n        }\r\n<\/pre>\n<p>We then use Rust&#8217;s match construct to compare and identify the chosen subcommand. If subcommand &#8220;clone&#8221; is passed from command-line, then it requires one mandatory argument to be passed as mandated by the num_args() attribute while constructing the subcommand. If no arguments are passed with &#8220;clone&#8221; subcommand then an invalid message is displayed.<\/p>\n<h6>Run command and expected outcome:<\/h6>\n<pre lang=\"Shell\">\r\n$ cargo run --bin subcommands -- -h\r\nUsage: subcommands.exe [COMMAND]\r\n\r\nCommands:\r\n  clone   Clone a repository\r\n  push    Push changes to a repository\r\n  commit  Commit changes to a repository\r\n  help    Print this message or the help of the given subcommand(s)\r\n\r\nOptions:\r\n  -h, --help  Print help\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin subcommands -- push --help\r\nPush changes to a repository\r\n\r\nUsage: subcommands.exe push [OPTIONS]\r\n\r\nOptions:\r\n  -b, --branch <branch>  branch name\r\n  -h, --help             Print help\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin subcommands -- push -b fix_issue#45\r\nPushing changes to branch: fix_issue#45\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin subcommands -- clone --name qxf2\/qxf2-page-object-model\r\nCloning repository with name: qxf2\/qxf2-page-object-model\r\n<\/pre>\n<pre lang=\"Shell\">\r\n$ cargo run --bin subcommands -- commit -m \"Fixed issue#45\"\r\nCommitting changes with message: Fixed issue#45\r\n<\/pre>\n<hr>\n<p>We have briefed you with examples highlighting the implementation of certain important features of Clap. We believe that we have taken your Rust Clap journey &#8211; a little further. Hope you found it useful and it gave you a head-start into implementing your CLI applications in Rust. Happy building and testing!<\/p>\n<hr>\n<h4>Hire Quality Engineers (QE) from Qxf2<\/h4>\n<p>Hire QEs from Qxf2 to experience a better form of testing. We employ experienced, technical and proactive testers. Most engineers we work with call us back when they switch jobs. They enjoy the fact that Qxf2 guides their testing efforts the right way instead of simply taking direction and executing surface level tests. As you can see from this post (and blog, really), we work on a number of technical techniques that help us understand the technology that our clients work with. This sort of insight leads to better testing and improved collaboration with developers. If you are looking for technical testers to join your team, <a href=\"https:\/\/qxf2.com\/contact?utm_source=Clap_little_further&#038;utm_medium=click&#038;utm_campaign=From%20blog\" rel=\"noopener\" target=\"_blank\">get in touch<\/a>. <\/p>\n<hr>\n","protected":false},"excerpt":{"rendered":"<p>At Qxf2 we are always on the lookout to go wider and deeper into evolving our technical toolbelt. One such initiative was to explore popular Rust crates and to come up with code samples showing implementation of important features. Clap is a popular crate in Rust, that aids in developing CLI applications seamlessly. In our opening blog, we gave you [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[413,402],"tags":[],"class_list":["post-21436","post","type-post","status-publish","format-standard","hentry","category-clap-rust","category-rust-cli"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/21436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=21436"}],"version-history":[{"count":23,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/21436\/revisions"}],"predecessor-version":[{"id":21555,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/21436\/revisions\/21555"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=21436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=21436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=21436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}