Rust製pastelコマンドにimas色機能を追加する

はじめに

Rust製pastelコマンドとは以下のやつです。

github.com

(gifはリポジトリから拝借)

pastel is a command-line tool to generate, analyze, convert and manipulate colors. It supports many different color formats and color spaces like RGB (sRGB), HSL, CIELAB, CIELCh as well as ANSI 8-bit and 24-bit representations.

つまり、CLI上で色に関することをいろいろできるということですね。
その"いろいろ"を実現するために、様々なサブコマンドがあります。
そこにimasを追加して、アイドルやユニットのイメージカラーを取得できるようにしてみました。

pastel 0.8.1
A command-line tool to generate, analyze, convert and manipulate colors

USAGE:
    pastel [OPTIONS] <SUBCOMMAND>

OPTIONS:
    -m, --color-mode <mode>              Specify the terminal color mode: 24bit, 8bit, off, *auto*
    -f, --force-color                    Alias for --mode=24bit
        --color-picker <color-picker>
            Use a specific tool to pick the colors [possible values: gpick, xcolor,
            grabc, colorpicker, chameleon, kcolorchooser]
    -h, --help                           Prints help information
    -V, --version                        Prints version information

SUBCOMMANDS:
    color         Display information about the given color
    list          Show a list of available color names
    random        Generate a list of random colors
    distinct      Generate a set of visually distinct colors
    sort-by       Sort colors by the given property
    pick          Interactively pick a color from the screen (pipette)
    format        Convert a color to the given format
    paint         Print colored text using ANSI escape sequences
    gradient      Generate an interpolating sequence of colors
    mix           Mix two colors in the given colorspace
    colorblind    Simulate a color under a certain colorblindness profile
    set           Set a color property to a specific value
    saturate      Increase color saturation by a specified amount
    desaturate    Decrease color saturation by a specified amount
    lighten       Lighten color by a specified amount
    darken        Darken color by a specified amount
    rotate        Rotate the hue channel by the specified angle
    complement    Get the complementary color (hue rotated by 180°)
    gray          Create a gray tone from a given lightness
    to-gray       Completely desaturate a color (preserving luminance)
    textcolor     Get a readable text color for the given background color
    imas          Get a Im@s idol's image color from im@sparql.      <---  こんな感じ!!!
    help          Prints this message or the help of the given subcommand(s)

↑ サブコマンドに追加

そして、出来上がったものがこちら

github.com

f:id:crssnky:20210801232936p:plain

こんな感じになります。
それでは解説していきます。

Rustからim@sparqlを叩く

im@sparqlへのクエリは以下になります。

REFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#>
PREFIX imasrdf: <https://sparql.crssnky.xyz/imasrdf/RDFs/detail/>

SELECT distinct ?c 
WHERE {
  imasrdf:{} imas:Color ?c
}

今回は軽めの実装にするため、im@sparql内のリソース名を入力してもらうようにします。ですのでimasrdf:{}{}には、アイドルやユニットのリソース名(Kisaragi_Chihayaやnoctchillなど)が入ります。

シリアライズの詳しい内容は過去の記事へ。だいたい一緒です。

crssnky.hatenablog.jp

imas.rsファイルをcommandsフォルダに作成して処理を作ります(コード)。
HTTP GETするURLは以下のように作ります。form_urlencodedを利用すれば、クエリストリングを良い感じにしてエンコードまでしてくれます。
また、ureq::get().call()であれば同期処理でコールします。今回のようなものは、コールバック書かなくて良いのでラクですね。

    let encoded_query = form_urlencoded::Serializer::new(String::new())
      .append_pair("output", "json")
      .append_pair("force-accept", "text/plain")
      .append_pair("query", &query)
      .finish();
    let sparql_url = format!(
      "https://sparql.crssnky.xyz/spql/imas/query?{}",
      encoded_query
    );

    let res = ureq::get(&sparql_url).call();
    if res.ok() {
・・・
    }

im@sparqlのレスポンスを解釈する

受け取ったJSONを構造体に変換し、カラーコードを抜き出してColor構造体に変えているだけです。u8::from_str_radixを使えば、Hex文字列から数値に変換してくれます。

      let json_str = res.into_string().unwrap();
      let res_json: Response = serde_json::from_str(&json_str).unwrap();
      let json = res_json.results.bindings;
      if json.len() > 0 {
        for data in json {
          let color_string = &*(data.c.value);
          if color_string.len() >= 6 {
            let red: u8 = u8::from_str_radix(&color_string[0..2], 16).unwrap();
            let green: u8 = u8::from_str_radix(&color_string[2..4], 16).unwrap();
            let blue: u8 = u8::from_str_radix(&color_string[4..6], 16).unwrap();
            let color = Color::from_rgb(red, green, blue);
            out.show_color(&config, &color)?;
          }
        }
      }

imasコマンドを追加する

まずは、mod.rsファイルです(コード)。
上部のmod郡にimasを追加します。また、imas:ImasCommandを使うよう指定します。

mod imas;
use imas::ImasCommand;

あとは、imasというサブコマンドを実行したときに、imas:ImasCommandが使われるようimpl Commandpub fn from_string()に指定します。

      "colorcheck" => Command::Generic(Box::new(ColorCheckCommand)),
      "imas" => Command::Generic(Box::new(ImasCommand)),   // これ
      _ => unreachable!("Unknown subcommand"),

次にcli.rsファイルです(コード
サブコマンドがたくさん並んでいますので、そこにimasコマンドの引数やヘルプを書いておきます。

.subcommand(
    SubCommand::with_name("imas")
        .about("Get a Im@s idol's image color from im@sparql.")
        .long_about("Get a Im@s idol's image color from im@sparql.\n\n\
        Example:\n  \
          pastel imas Kisaragi_Chihaya")
        .arg(color_arg.clone()),
)

おわりに

以上が解説です。これを応用すれば、色に関する様々なアプリケーションを作成できると思います。
特に元から実装されている可視化部分がよく出来ています。imasコマンドも色を取ってきて、Color構造体に入れるだけで良い感じに可視化されるため、色を取得する部分だけでもカスタムすると面白いと思います。