カラーコードを渡すと色のプレビューをしてくれるコマンドラインツールを作った
この記事は以下のページに移転しました.
引数としてカラーコードを渡すと,指定した色のプレビューをしてくれるコマンドラインツールを Rust で書いた.
もちろん 24-bit color 対応の端末エミュレータを使っていないとちゃんと動かない.2018年上半期に作った役に立たないものランキング第一位.
モチベーション
思い付いたので作った.最近は Google でさえカラーコードを打つと色を教えてくれる時代なので,実用性は皆無.「コマンドラインツールと言えばとりあえず Go で書く」みたいなご時世なので,「たまには Go でも書くか〜〜」という気持ちで最初は Go で書いていたが,途中からあまりにつらくなってきたので*1シュッと Rust で書き直した.
コマンドラインオプションパーザ
コマンドラインオプションのパーズ処理を自分で書きたいわけではなかったので,clap というライブラリを用いた.私は目的にフォーカスする男である.
いくつか usage があるが,今回は一番過激(?)と思われる,YAML を読み込む方式にした.
まず,以下のような YAML ファイルを用意しておく.
name: farbe version: "0.1.0" author: Ryota Kameoka <kameoka.ryota@gmail.com> about: Command-line color preview tool settings: - ArgRequiredElseHelp args: - hex: long: hex value_name: HEX help: 6-digit hexadecimal color code (e.g. \#123456) takes_value: true - red: short: r long: red help: Red component of the color, used along with --green and --blue takes_value: true - green: short: g long: green help: Green component of the color, used along with --red and --blue takes_value: true - blue: short: b long: blue help: Blue component of the color, used along with --red and --green takes_value: true - width: short: w long: width help: Width of the colored block takes_value: true - height: short: h long: height help: width of the colored block takes_value: true
次に,load_yaml!
というマクロが提供されているので,これに上記のファイルのパス (以下では仮に cli.yml
としている) を渡す.
let yaml = load_yaml!("./cli.yml"); let app = App::from_yaml(yaml); let matches = app.get_matches();
すると,コンパイル時に YAML が読み込まれ,いい感じにしてくれる.自分で --version
や --help
の実装を書く必要もない.あとは matches.is_present()
とか matches.value_of()
とかでよしなに.
パーザコンビネータ
カラーコードを読む必要があったので,regex でやっていってもよかったが,「Rust やったらパーザコンビネータぐらいいい感じのやつあるやろ〜」と思って調べると,nom というライブラリを見つけたので,これを採用した.
README を読んでいたら,カラーコードをパーズするというお誂え向きなコードが Example セクションに書かれていた.以下はその引用.
#[macro_use] extern crate nom; #[derive(Debug,PartialEq)] pub struct Color { pub red: u8, pub green: u8, pub blue: u8, } fn from_hex(input: &str) -> Result<u8, std::num::ParseIntError> { u8::from_str_radix(input, 16) } fn is_hex_digit(c: char) -> bool { match c { '0'..='9' | 'a'..='f' | 'A'..='F' => true, _ => false, } } named!(hex_primary<&str, u8>, map_res!(take_while_m_n!(2, 2, is_hex_digit), from_hex) ); named!(hex_color<&str, Color>, do_parse!( tag!("#") >> red: hex_primary >> green: hex_primary >> blue: hex_primary >> (Color { red, green, blue }) ) ); #[test] fn parse_color() { assert_eq!(hex_color("#2F14DF"), Ok(("", Color { red: 47, green: 20, blue: 223, }))); }
map_res!
とかその辺可読性ちょっとキツくないか,などとは思うものの,まぁ便利.
久し振りに Rust を書いて
Rust は v1.0.0 alpha くらいのときからしばらく watch していて,その後はあまり追っていなかったのだが,久し振りに触ってみると,マクロが広く*2活用されていることに驚いた.Template Haskell は GHC の独自拡張に過ぎないが,Rust は言語のコアに含まれているからだろうか.今回利用した clap
nom
ともに,結構アグレッシヴな使い方をしていて,すごい,となった (こなみかん)
まとめ
Rust でコマンドラインツール,ええんちゃう?