Clap製CLIツールでtab補完スクリプトを生成して.debでインストールできるようにする
Clap製CLIツールでtab補完スクリプトを生成して.debでインストールできるようにする記事です。
tab補完スクリプトを生成する
今回は例としてこんな感じのツールのtab補完スクリプトを生成してみようと思います。
use std::path::PathBuf;
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Parser)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Command1 description
Command1 {
#[clap(long, help = "String argument")]
arg1: String,
#[clap(value_enum, long, help = "MyEnum variant")]
arg2: MyEnum,
#[clap(long, help = "Path to a directory")]
arg3: PathBuf,
#[clap(long, help = "Optional argument")]
arg4: Option<String>,
},
/// Command2 description
Command2 {
// none
},
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
enum MyEnum {
Variant1,
Variant2,
}
fn main() {
let args = Cli::parse();
match args.command {
Commands::Command1 {
arg1,
arg2,
arg3,
arg4,
} => {
println!(
"Command1: arg1={}, arg2={:?}, arg3={:?}, arg4={:?}",
arg1, arg2, arg3, arg4
);
}
Commands::Command2 {} => {
println!("Command2");
}
}
}
1. コマンドの定義部分を別ファイルに切り出す
ここではsrc/commands.rsに切り出してみます。
src/commands.rs
use clap::{Parser, Subcommand, ValueEnum, ValueHint};
use std::path::PathBuf;
#[derive(Parser)]
pub struct Cli {
#[clap(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Command1 description
Command1 {
#[clap(long, help = "String argument")]
arg1: String,
#[clap(value_enum, long, help = "MyEnum variant")]
arg2: MyEnum,
#[clap(long, help = "Path to a directory")]
arg3: PathBuf,
#[clap(long, help = "Optional argument")]
arg4: Option<String>,
},
/// Command2 description
Command2 {
// none
},
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum MyEnum {
Variant1,
Variant2,
}
src/main.rs
mod commands;
use clap::Parser;
use commands::{Cli, Commands};
fn main() {
let args = Cli::parse();
match args.command {
Commands::Command1 {
arg1,
arg2,
arg3,
arg4,
} => {
println!(
"Command1: arg1={}, arg2={:?}, arg3={:?}, arg4={:?}",
arg1, arg2, arg3, arg4
);
}
Commands::Command2 {} => {
println!("Command2");
}
}
}
main.rsから参照可能にするために適宜pub
をつけるのを忘れないでください。
2. 一部のargにvalue_hintをつける
+ use clap::ValueHint;
pub enum Commands {
Command1 {
#[clap(
long,
help = "Path to a directory",
+ value_hint = ValueHint::DirPath,
)]
arg3: PathBuf,
}
}
例えばこんな感じにすると、arg3のtab補完にはファイルへのパスは出なくなり、ディレクトリへのパスだけが表示されるようになります。
3. build.rsでtab補完スクリプトを生成する
Rustには、プロジェクトのルートにbuild.rsを準備しておくとビルド時に自動で実行してくれる機能があります。これを使ってビルドするたびにtab補完スクリプトを生成するようにします。
tab補完スクリプトはclap_complete
クレートを使うと生成できます。
build時のdependenciesは通常時のdependenciesとは完全に別で扱われるため、とりあえずCargo.tomlに以下を追加します
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
+ [build-dependencies]
+ clap = {version = "4.5.16", features = ["derive"]}
+ clap_complete = "4.5.22"
[dependencies]
clap = {version = "4.5.16", features = ["derive"]}
(commands.rsでclap以外のdependencyを使っている場合はそれも追加してください)
次にプロジェクトのルートにbuild.rsを設置します。(src配下ではなくプロジェクトのルートです)
use std::{env, fs, io};
use clap::{CommandFactory, ValueEnum};
use clap_complete::Shell;
#[path = "src/commands.rs"]
mod commands;
use commands::Cli;
fn main() -> io::Result<()> {
fs::create_dir_all("dist/completions")?;
for &shell in Shell::value_variants() {
clap_complete::generate_to(
shell,
&mut Cli::command(),
env!("CARGO_PKG_NAME"),
"dist/completions",
)?;
}
Ok(())
}
#[path = "src/commands.rs"]
mod commands;
ここでbuild.rsからcommands.rsを読みに行って使えるようにしています。build.rsがsrc配下ではなくプロジェクトのルートにあるため、#[path = "src/commands.rs"]
が必要になっています。
fs::create_dir_all("dist/completions")?;
ここで(無ければ)completionを出力するディレクトリを作成して、
for &shell in Shell::value_variants() {
clap_complete::generate_to(
shell,
&mut Cli::command(),
env!("CARGO_PKG_NAME"),
"dist/completions",
)?;
}
ここでclap_completeを使って各シェルについてtab補完スクリプトを生成しています。
.debパッケージでインストールできるようにする
Rustで作ったバイナリを.debパッケージ化するツールにcargo-debというものがあります
単純に.debパッケージ化するだけならcargo install cargo-deb
して、Cargo.tomlにlicense
やらauthors
やらを指定してcargo deb
すればもう動きます
ただ、これだけだとtab補完まではインストールできません
そのための設定をもう少し書き足します
[package.metadata.deb]
assets = [
["target/release/foo", "usr/bin/foo", "755"],
["dist/completions/foo.bash", "usr/share/bash-completion/completions/foo.bash", "644"],
["dist/completions/foo.fish", "usr/share/fish/vendor_completions.d/foo.fish", "664"],
["dist/completions/_foo", "usr/share/zsh/vendor-completions/_foo", "644"],
]
(fooのところはおのおののCARGO_PKG_NAMEに置き換えてください)
こんな感じで指定してやると自動でインストールしてくれるようになります
["target/release/foo", "usr/bin/foo", "755"],
を忘れないように注意してください(デフォルトのassetsを上書きしてしまうためこれも必要になる)
完成
完成です🎉
Discussion