276 lines
5.6 KiB
Markdown
276 lines
5.6 KiB
Markdown
|
+++
|
||
|
date = "2020-05-12JST"
|
||
|
tags = ["rust"]
|
||
|
title = "rustで作るcli tool"
|
||
|
slug = "rust"
|
||
|
+++
|
||
|
|
||
|
私は、cli(command line interface) toolを好んで使うのですが、自分で作ることもあります。
|
||
|
|
||
|
特に、ワンバイナリなtoolは、CIやdockerを回すときに便利でgolangをよく使います。goはshellとの親和性が高いので色々な場所で扱いやすく、非常に素晴らしい言語です。
|
||
|
|
||
|
ただ、最近は、rustが人気みたい。理由としては、高速で、サイズが小さく、安全であることが挙げられます。デメリットはコンパイルが遅いこと。
|
||
|
|
||
|
しかし、rustも非常に魅力なので、`base64 {encode,decode}`などをcli tool化しながら、rustを学んでいこうという記事です。
|
||
|
|
||
|
まず、テンプレを作成します。`src/main.rs`が本体になります。
|
||
|
|
||
|
```sh
|
||
|
$ cargo new udrs
|
||
|
$ cd udrs
|
||
|
$ vim src/main.rs
|
||
|
```
|
||
|
|
||
|
ここで、cli optionなどを書きやすくするframeworkの[seahorse](https://github.com/ksk001100/seahorse)を導入します。
|
||
|
|
||
|
```rust:src/main.rs
|
||
|
use std::env;
|
||
|
use seahorse::{App, Command, Context};
|
||
|
|
||
|
fn main() {
|
||
|
let args: Vec<String> = env::args().collect();
|
||
|
let app = App::new()
|
||
|
.name(env!("CARGO_PKG_NAME"))
|
||
|
.author(env!("CARGO_PKG_AUTHORS"))
|
||
|
.version(env!("CARGO_PKG_VERSION"))
|
||
|
.usage("cli_tool [command] [x] [y]")
|
||
|
.command(
|
||
|
Command::new()
|
||
|
.name("t")
|
||
|
.usage("udrs t {}")
|
||
|
.action(t),
|
||
|
);
|
||
|
app.run(args);
|
||
|
}
|
||
|
|
||
|
fn t(_c: &Context) {
|
||
|
let t = "hello world.";
|
||
|
println!("{}",t);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```toml:Cargo.toml
|
||
|
[dependencies]
|
||
|
seahorse = "0.7.1"
|
||
|
```
|
||
|
|
||
|
では、実行してみましょう。ここでは、オプションを`t`で指定しています。
|
||
|
|
||
|
```sh
|
||
|
$ cargo run t
|
||
|
hello world.
|
||
|
```
|
||
|
|
||
|
次に、引数をbase64でencode, decodeするオプションを実装してみようと思います。
|
||
|
|
||
|
```toml:Cargo.toml
|
||
|
[dependencies]
|
||
|
base64 = "0.9.2"
|
||
|
```
|
||
|
|
||
|
```rust:src/main.rs
|
||
|
use std::env;
|
||
|
use seahorse::{App, Command, Context};
|
||
|
|
||
|
fn main() {
|
||
|
let args: Vec<String> = env::args().collect();
|
||
|
let app = App::new()
|
||
|
.name(env!("CARGO_PKG_NAME"))
|
||
|
.author(env!("CARGO_PKG_AUTHORS"))
|
||
|
.version(env!("CARGO_PKG_VERSION"))
|
||
|
.usage("cli_tool [command] [x] [y]")
|
||
|
.command(
|
||
|
Command::new()
|
||
|
.name("t")
|
||
|
.usage("udrs t {}")
|
||
|
.action(t),
|
||
|
)
|
||
|
.command(
|
||
|
Command::new()
|
||
|
.name("e")
|
||
|
.usage("udrs e {}")
|
||
|
.action(e),
|
||
|
)
|
||
|
.command(
|
||
|
Command::new()
|
||
|
.name("d")
|
||
|
.usage("udrs d {}")
|
||
|
.action(d),
|
||
|
);
|
||
|
app.run(args);
|
||
|
}
|
||
|
|
||
|
fn t(_c: &Context) {
|
||
|
let t = "hello world.";
|
||
|
println!("{}",t);
|
||
|
}
|
||
|
|
||
|
fn e(c: &Context) {
|
||
|
println!("{}", base64::encode(&c.args[0]));
|
||
|
}
|
||
|
|
||
|
//fn d(c: &Context) {
|
||
|
// println!("{:?}", &base64::decode(&c.args[0]).unwrap());
|
||
|
//}
|
||
|
fn d(c: &Context) {
|
||
|
let by = base64::decode(&c.args[0]).unwrap();
|
||
|
let res = by.iter().map(|&s| s as char).collect::<String>();
|
||
|
println!("{:?}",res);
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
これで、`e,d`オプションが追加されました。base64でencodeしてみましょう。
|
||
|
|
||
|
```sh
|
||
|
$ cargo run e "hello world."
|
||
|
aGVsbG8gd29ybGQu
|
||
|
```
|
||
|
|
||
|
次は、decodeしてみます。
|
||
|
|
||
|
```sh
|
||
|
$ cargo run d "aGVsbG8gd29ybGQu"
|
||
|
hello world.
|
||
|
```
|
||
|
|
||
|
うまくいきました。
|
||
|
|
||
|
testも追加してみましょう。
|
||
|
|
||
|
```rust:src/main.rs
|
||
|
...
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
#[test]
|
||
|
fn base64_encode() {
|
||
|
let expected = "aGVsbG8gd29ybGQu";
|
||
|
let actual = base64::encode("hello world.");
|
||
|
assert_eq!(expected, actual);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```sh
|
||
|
$ cargo test
|
||
|
test tests::base64_encode ... ok
|
||
|
```
|
||
|
|
||
|
面倒なコマンドは、makefile化することもできます。
|
||
|
|
||
|
```makefile
|
||
|
LOG_LEVEL := debug
|
||
|
APP_ARGS := "hello world."
|
||
|
export RUST_LOG=url=$(LOG_LEVEL)
|
||
|
PREFIX := $(HOME)/.cargo
|
||
|
|
||
|
run:
|
||
|
cargo run $(APP_ARGS)
|
||
|
|
||
|
test:
|
||
|
cargo test
|
||
|
|
||
|
check:
|
||
|
cargo check $(OPTION)
|
||
|
|
||
|
install:
|
||
|
cargo install --force --root $(PREFIX) --path .
|
||
|
```
|
||
|
|
||
|
```sh
|
||
|
$ make run
|
||
|
help
|
||
|
```
|
||
|
|
||
|
runだけでなく、buildしてコマンドの挙動を確認してみましょう。
|
||
|
|
||
|
```sh
|
||
|
$ cargo build
|
||
|
$ ./target/debug/udrs
|
||
|
```
|
||
|
|
||
|
バイナリの配布は、travis-ciでbuildして、github releasesにdeployします。
|
||
|
|
||
|
```yml:.travis.yml
|
||
|
dist: trusty
|
||
|
language: rust
|
||
|
services: docker
|
||
|
rust:
|
||
|
- stable
|
||
|
sudo: required
|
||
|
env:
|
||
|
global:
|
||
|
- NAME=udrs
|
||
|
|
||
|
matrix:
|
||
|
include:
|
||
|
- env: TARGET=x86_64-unknown-linux-musl
|
||
|
- env: TARGET=x86_64-apple-darwin
|
||
|
os: osx
|
||
|
- env: TARGET=x86_64-pc-windows-gnu
|
||
|
|
||
|
before_install:
|
||
|
- rustup self update
|
||
|
|
||
|
install:
|
||
|
- source ~/.cargo/env
|
||
|
- cargo install --force cross
|
||
|
|
||
|
script:
|
||
|
- cross test --target $TARGET --release
|
||
|
|
||
|
before_deploy:
|
||
|
- cross build --target $TARGET --release
|
||
|
- bin=$NAME
|
||
|
- if [[ $TARGET = "x86_64-pc-windows-gnu" ]]; then bin=$NAME.exe; fi
|
||
|
- tar czf $NAME-$TRAVIS_TAG-$TARGET.tar.gz -C target/$TARGET/release $bin
|
||
|
|
||
|
deploy:
|
||
|
api_key:
|
||
|
secure: "xxx"
|
||
|
file_glob: true
|
||
|
file: $NAME-$TRAVIS_TAG-$TARGET.*
|
||
|
on:
|
||
|
tags: true
|
||
|
provider: releases
|
||
|
skip_cleanup: true
|
||
|
|
||
|
cache: cargo
|
||
|
before_cache:
|
||
|
- chmod -R a+r $HOME/.cargo
|
||
|
|
||
|
branches:
|
||
|
only:
|
||
|
- /^v?\d+\.\d+\.\d+.*$/
|
||
|
- master
|
||
|
```
|
||
|
|
||
|
`secure: "xxx"`は、`travis`コマンドで発行します。`github-access-token`をgetして以下のコマンドを実行。
|
||
|
|
||
|
```sh
|
||
|
$ sudo gem i travis
|
||
|
$ travis login
|
||
|
$ travis encrypt $GITHUB_ACCESS_TOKEN
|
||
|
|
||
|
$ cat .travis.yml
|
||
|
deploy:
|
||
|
api_key:
|
||
|
secure: "xxx"
|
||
|
```
|
||
|
|
||
|
tagをpushすると、travisが走ります。
|
||
|
|
||
|
```sh
|
||
|
$ git add .
|
||
|
$ git commit -m "first"
|
||
|
$ git push origin master
|
||
|
|
||
|
$ git tag -a "0.1.0" -m "v0.1.0"
|
||
|
$ git push origin --tags
|
||
|
```
|
||
|
|
||
|
https://github.com/syui/udrs
|
||
|
|
||
|
https://qiita.com/syui/items/e071ba72ea82d583e380
|