engl-2311-blog/blog/using-clap.html
2024-11-12 15:37:32 -06:00

203 lines
19 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta content="width=device-width, initial-scale=1" charset="utf-8" />
<title>Using `clap`</title>
<link href="/style.css" type="text/css" rel="stylesheet" />
<link href="/prism.css" type="text/css" rel="stylesheet" />
</head>
<body class="line-numbers">
<h1 id="using-clap">Using <code>clap</code></h1>
<p>Clap stands for Command Line Argument Parser, and put simply,
it's a great library for making command-line stuff with Rust.
Even Cargo, Rust's package manager, depends on it
(<em>Cargo/cargo.toml at master</em>), and it's been downloaded
over 300 million times (<em>Clap - Crates.io: Rust package
registry</em>).</p>
<p>Rather than going over everything clap can do, I'll go over
how I've used it in my <code>disk-read-benchmark</code> program
I'll be using in my next blog post.</p>
<h2 id="basics">Basics</h2>
<p>First off, we need to install <code>clap</code>; make sure to
enable its <code>derive</code> feature as that's what we'll be
using.</p>
<div class="sourceCode" id="cb1"><pre
class="language-sh"><code class="language-bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">cargo</span> add clap <span class="at">--features</span> derive</span></code></pre></div>
<p>First off, we need to get a bit of code just to start
off:</p>
<div class="sourceCode" id="cb2"><pre
class="language-rust"><code class="language-rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">clap::</span><span class="op">{</span>Parser<span class="op">,</span> Subcommand<span class="op">};</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>derive<span class="at">(</span>Parser<span class="at">)]</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>command<span class="at">(</span>version<span class="op">,</span> about<span class="op">,</span> long_about <span class="op">=</span> <span class="cn">None</span><span class="at">)]</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">struct</span> Cli <span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="at">#[</span>command<span class="at">(</span>subcommand<span class="at">)]</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">pub</span> command<span class="op">:</span> Commands<span class="op">,</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This has the built-in "version" and "about" options, with the
long "about" option disabled.</p>
<p>Next, we need to list all out commands we'll have:</p>
<div class="sourceCode" id="cb3"><pre
class="language-rust"><code class="language-rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>derive<span class="at">(</span>Subcommand<span class="at">)]</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">enum</span> Commands <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="co">///Run this thing</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> Run<span class="op">,</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="co">///Delete the stuff that thing does</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> Delete<span class="op">,</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The documentation comments (<code>///</code>) should
<em>not</em> have a space after the slashes, as otherwise the
program will have an extra space where it shouldn't.</p>
<p>Finally, we create the <code>main()</code> function. First it
parses everything, then checks what command was run and runs the
relevant code.</p>
<div class="sourceCode" id="cb4"><pre
class="language-rust"><code class="language-rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> main() <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> cli <span class="op">=</span> <span class="pp">Cli::</span>parse()<span class="op">;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> cli<span class="op">.</span>command <span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="pp">Commands::</span>Run <span class="op">{</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> run()<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="pp">Commands::</span>Delete <span class="op">{</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> delete()<span class="op">;</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>That's all you need to know to use <code>clap</code> at a
very basic level; for more details, check out the docs (<em>clap
Documentation</em>). But, you probably don't want to have to
type in the entire command automatically, autocomplete would be
nice. So I'll also go over how to use <code>clap_complete</code>
as well.</p>
<h2 id="clap_complete"><code>clap_complete</code></h2>
<p>Searching through the documentation (<em>Clap_complete
Documentation</em>), you'll notice that the docs don't cover how
to use it with clap's derive at all. Instead, after some
Googling, I found an example script in <em>clap</em>'s
repository (<em>completion-derive.rs at master</em>), which I
then adapted and played around with a bit until I got it figured
out.</p>
<p>Anyways, again, we need to install <code>clap_complete</code>
first:</p>
<div class="sourceCode" id="cb5"><pre
class="language-sh"><code class="language-bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">cargo</span> add clap_complete</span></code></pre></div>
<p>Then, add the relevant imports. We'll just being doing it for
the fish shell since that's what I use, so we'll only import
<code>Fish</code>; Bash, Zsh, PowerShell, and Elvish are also
supported.</p>
<div class="sourceCode" id="cb6"><pre
class="language-rust"><code class="language-rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">clap_complete::aot::</span><span class="op">{</span>generate<span class="op">,</span> Fish<span class="op">};</span></span></code></pre></div>
<p>Then, we need to add a command to generate the
completion:</p>
<div class="sourceCode" id="cb7"><pre
class="language-rust"><code class="language-rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>derive<span class="at">(</span>Subcommand<span class="at">)]</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">enum</span> Commands <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="co">///Run this thing</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> Run<span class="op">,</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="co">///Delete the stuff that thing does</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> Delete<span class="op">,</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="co">///Generate fish completions</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> FishCompletions<span class="op">,</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Next, we actually generate the completion, adding it like
it's another command:</p>
<div class="sourceCode" id="cb8"><pre
class="language-rust"><code class="language-rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a> <span class="cf">match</span> cli<span class="op">.</span>command <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="pp">Commands::</span>Run <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> run()<span class="op">;</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="pp">Commands::</span>Delete <span class="op">{</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> delete()<span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="pp">Commands::</span>GenerateFishCompletions <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> generate(</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> Fish<span class="op">,</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span><span class="kw">mut</span> <span class="pp">Cli::</span>command()<span class="op">,</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;example-program&quot;</span><span class="op">,</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="op">&amp;</span><span class="kw">mut</span> stdout()<span class="op">,</span></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> )<span class="op">;</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div>
<p>To explain the options for <code>generate()</code>:</p>
<ul>
<li><code>Fish</code>: The shell we're using.</li>
<li><code>&amp;mut Cli::command()</code>: I don't actually know
what this does, but understanding ths library fully this is
beyond my pay grade, especially given the somewhat lacking
docs.</li>
<li><code>"example-program"</code>: The name of our program</li>
<li><code>&amp;mut stdout()</code>: <code>stdout</code>, so that
it can print the completions. Why does it do it this way? I
don't know, it doesn't make sense to me. Why doesn't it just
return it as a String? I don't know. But it works, I
suppose.</li>
</ul>
<p>As an example of all this, here's my
<code>disk-read-benchmark</code> program, running using all
this. The commands have formatting I can't do, so it looks even
better than I can even show here.</p>
<div class="sourceCode" id="cb9"><pre
class="language-txt"><code class="language-default"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>~&gt; disk-read-benchmark</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>Usage: disk-read-benchmark &lt;COMMAND&gt;</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>Commands:</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> generate-bash-completions Generate bash completions</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> generate-zsh-completions Generate zsh completions</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> generate-fish-completions Generate fish completions</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> grab-data Grabs the datasets used for benchmarking</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> benchmark Runs the benchmark</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> prep-dirs Prepares the directories so other programs can prepare their datasets</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> run Runs it all</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a> help Print this message or the help of the given subcommand(s)</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a>Options:</span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a> -h, --help Print help</span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> -V, --version Print version</span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a>~&gt; disk-read-benchmark generate-fish-completions | source</span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a>~&gt; disk-read-benchmark benchmark --help</span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a>Runs the benchmark</span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true" tabindex="-1"></a>Usage: disk-read-benchmark benchmark</span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-23"><a href="#cb9-23" aria-hidden="true" tabindex="-1"></a>Options:</span>
<span id="cb9-24"><a href="#cb9-24" aria-hidden="true" tabindex="-1"></a> -h, --help Print help</span></code></pre></div>
<p>To better see how great it looks, here's a screenshot:</p>
<img src="/assets/using-clap/1.png"
title="The same output, but with very nice formatting - underlining and bolding for headers and the tables" alt="The same output, but with very nice formatting - underlining and bolding for headers and the tables" />
<p>Pressing tab twice after entering
<code>disk-read-benchmark</code> displays the completions, which
I can select and use like any other program's.</p>
<div class="sourceCode" id="cb10"><pre
class="language-txt"><code class="language-default"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>~&gt; disk-read-benchmark</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>benchmark (Runs the benchmark) grab-data (Grabs the datasets used for benchmarking)</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>generate-bash-completions (Generate bash completions) help (Print this message or the help of the given subcommand(s))</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>generate-fish-completions (Generate fish completions) prep-dirs (Prepares the directories so other programs can prepare their datasets)</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>generate-zsh-completions (Generate zsh completions) run (Runs it all)</span></code></pre></div>
<h2 id="sources">Sources</h2>
<p>- clap contributors. “clap Documentation.” Clap - Rust, <a
href="https://docs.rs/clap/latest/clap/">docs.rs/clap/latest/clap/</a>.
Accessed 12 Nov. 2024.<br />
- clap_complete contributors. “Clap_complete Documentation.”
Clap_complete - Rust, <a
href="https://docs.rs/clap_complete/latest/clap_complete">docs.rs/clap_complete/latest/clap_complete</a>.
Accessed 12 Nov. 2024.<br />
- clap contributors.
“clap/clap_complete/examples/completion-derive.rs at master ·
Clap-Rs/Clap.” GitHub, <a
href="https://github.com/clap-rs/clap/blob/master/clap_complete/examples/completion-derive.rs">github.com/clap-rs/clap/blob/master/clap_complete/examples/completion-derive.rs</a>.
Accessed 12 Nov. 2024.<br />
- cargo contributors. “cargo/Cargo.Toml at master ·
rust-lang/cargo.” GitHub, <a
href="https://github.com/rust-lang/cargo/blob/master/Cargo.toml">github.com/rust-lang/cargo/blob/master/Cargo.toml</a>.
Accessed 12 Nov. 2024.<br />
- “Clap - Crates.io: Rust Package Registry.” crates.io,
crates.io/crates/clap. Accessed 12 Nov. 2024.</p>
<iframe src="https://john.citrons.xyz/embed?ref=askiiart.net" style="margin-left:auto;display:block;margin-right:auto;max-width:732px;width:100%;height:94px;border:none;"></iframe>
<script src="/prism.js"></script>
</body>
<footer>
<p><a href="https://git.askiiart.net/askiiart/engl-2311-blog">Source code</a>&ensp;|&ensp;<a href="/feed.xml">RSS</a>&ensp;|&ensp;<a href="/glossary.html">Glossary</a>&ensp;|&ensp;<a href="/about.html">About</a></p>
<small>Image captions are the same as the alt text; assuming you're sighted, you can most likely ignore them.</small>
</footer>
</html>