RustでAlfredのWorkflowを作って業務効率化

# はじめに
Macユーザーなら一度は耳にしたことであろう、そして多くの方が利用されているであろうAlfred。
かくいう私もAlfredユーザーのひとりです。
Alfredは無料でも使えますが、PowerPackを購入するとより強力な機能が使えるようになります。
Alfred Workflowsを使えばAlfredを自分好みにカスタマイズできます。
たとえば、[Time Zones](https://jhartman.pl/2016/12/14/alfred-3-workflow-time-zone/)では各国の現在時刻がチェックできたり、時刻変換ができます。
<img src="https://camo.githubusercontent.com/78d981f9957df181c386f760dbb58fa8e2a9d07b/68747470733a2f2f6a686172746d616e2e706c2f77702d636f6e74656e742f75706c6f6164732f323031362f31322f416c66726564322e706e67" width="500">

<img src="https://camo.githubusercontent.com/ed22093086a1e82d4e67dd7c7527a650d1a0f1e0/68747470733a2f2f6a686172746d616e2e706c2f77702d636f6e74656e742f75706c6f6164732f323031362f31322f416c66726564332e706e67" width="500">
出典: https://github.com/jaroslawhartman/TimeZones-Alfred

私が一番使うWorkflowは[alfred-emoji](https://github.com/jsumners/alfred-emoji)です。
リモートワークに移行してから使う機会が増えました。
<img src="https://raw.githubusercontent.com/jsumners/alfred-emoji/master/images/screenshot.png" width="500">
出典: https://github.com/jsumners/alfred-emoji

# 業務効率化とWorkflow
仕事をしていると、たとえば郵便番号のようなコードを住所に変換したり、住所から郵便番号に変換したい場面がしばしばあります。
そんなときはwikiを開いてページ内検索をして欲しい情報にアクセスしていました。
この作業が手間だったので手軽に検索できるWorkflowを作って業務効率化しました。

Workflowの作り方は↓の記事がわかりやすいです。
[Alfred 4でのWorkflowの作り方とWorkflow Objectのまとめ – Webrandum](https://webrandum.net/alfred4-how-to-create-workflow/)

業務効率化のために作ったWorkflowには公開できない情報も含まれているので、検索対象のデータを元素に入れ替えて紹介します。

# 成果物
元素記号をキーワードに元素名を検索し、検索結果から選択した元素名をクリップボードにコピーできるWorkflowを作りました。
![スクリーンショット 2020-08-11 10.50.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/3a10d483-696c-f84e-a873-dada16aa96cf.png)

# コード
WorkflowのScript Filterの出力は↓のようなフォーマットで出力する必要があります。

```json
{"items": [
    {
        "uid": "desktop",
        "type": "file",
        "title": "Desktop",
        "subtitle": "~/Desktop",
        "arg": "~/Desktop",
        "autocomplete": "Desktop",
        "icon": {
            "type": "fileicon",
            "path": "~/Desktop"
        }
    }
]}
```
出典: [Script Filter JSON Format - Workflow Input Objects - Alfred Help and Support](https://www.alfredapp.com/help/workflows/inputs/script-filter/json/)

`alfred`というCrateを使うと、↓のように書けばフォーマットに則ったJSONを出力してくれます。

```rust
alfred::json::write_items(io::stdout(), &[
    alfred::Item::new("Item 1"),
    alfred::ItemBuilder::new("Item 2")
                        .subtitle("Subtitle")
                        .into_item(),
    alfred::ItemBuilder::new("Item 3")
                        .arg("Argument")
                        .subtitle("Subtitle")
                        .icon_filetype("public.folder")
                        .into_item()
])
```
[alfred - Rust](https://docs.rs/alfred/4.0.2/alfred/index.html)

### ディレクトリの構成

```
.
├── Cargo.lock
├── Cargo.toml
└──  src
    ├── elements.rs
    └── main.rs
```

### Cargo.toml

```toml:Cargo.toml
[dependencies]
regex = "1.3.9"
alfred = "4.0.2"
```

### main.rs

```rust:main.rs
use alfred;
use regex::Regex;
use std::env;
use std::io;

mod elements;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() == 1 {
        return;
    }
    let query = &args[1];

    let hm = elements::get_hashmap();
    let re = Regex::new(query).unwrap();
    let items = hm
        .keys()
        .filter(|&k| re.is_match(k))
        .map(|&k| {
            alfred::ItemBuilder::new(k)
                .arg(hm[k])
                .title(hm[k])
                .subtitle(k)
                .into_item()
        })
        .collect::<Vec<alfred::Item>>();

    let _ = alfred::json::write_items(io::stdout(), &items);
}
```


### elements.rs

元素名・元素記号は[元素の一覧 - Wikipedia](https://ja.wikipedia.org/wiki/%E5%85%83%E7%B4%A0%E3%81%AE%E4%B8%80%E8%A6%A7)のデータを使いました。

```rust:elements.rs
use std::collections::HashMap;

pub fn get_hashmap() -> HashMap<&'static str, &'static str> {
  [
    ("H", "水素"),
    ("He", "ヘリウム"),
    ("Li", "リチウム"),
    ("Be", "ベリリウム"),
    ("B", "ホウ素"),
    ("C", "炭素"),
    ("N", "窒素"),
    ("O", "酸素"),
    ("F", "フッ素"),
    ("Ne", "ネオン"),
    ("Na", "ナトリウム"),
    ("Mg", "マグネシウム"),
    ("Al", "アルミニウム"),
    ("Si", "ケイ素"),
    ("P", "リン"),
    ("S", "硫黄"),
    ("Cl", "塩素"),
    ("Ar", "アルゴン"),
    ("K", "カリウム"),
    ("Ca", "カルシウム"),
    ("Sc", "スカンジウム"),
    ("Ti", "チタン"),
    ("V", "バナジウム"),
    ("Cr", "クロム"),
    ("Mn", "マンガン"),
    ("Fe", "鉄"),
    ("Co", "コバルト"),
    ("Ni", "ニッケル"),
    ("Cu", "銅"),
    ("Zn", "亜鉛"),
    ("Ga", "ガリウム"),
    ("Ge", "ゲルマニウム"),
    ("As", "ヒ素"),
    ("Se", "セレン"),
    ("Br", "臭素"),
    ("Kr", "クリプトン"),
    ("Rb", "ルビジウム"),
    ("Sr", "ストロンチウム"),
    ("Y", "イットリウム"),
    ("Zr", "ジルコニウム"),
    ("Nb", "ニオブ"),
    ("Mo", "モリブデン"),
    ("Tc", "テクネチウム"),
    ("Ru", "ルテニウム"),
    ("Rh", "ロジウム"),
    ("Pd", "パラジウム"),
    ("Ag", "銀"),
    ("Cd", "カドミウム"),
    ("In", "インジウム"),
    ("Sn", "スズ"),
    ("Sb", "アンチモン"),
    ("Te", "テルル"),
    ("I", "ヨウ素"),
    ("Xe", "キセノン"),
    ("Cs", "セシウム"),
    ("Ba", "バリウム"),
    ("La", "ランタン"),
    ("Ce", "セリウム"),
    ("Pr", "プラセオジム"),
    ("Nd", "ネオジム"),
    ("Pm", "プロメチウム"),
    ("Sm", "サマリウム"),
    ("Eu", "ユウロピウム"),
    ("Gd", "ガドリニウム"),
    ("Tb", "テルビウム"),
    ("Dy", "ジスプロシウム"),
    ("Ho", "ホルミウム"),
    ("Er", "エルビウム"),
    ("Tm", "ツリウム"),
    ("Yb", "イッテルビウム"),
    ("Lu", "ルテチウム"),
    ("Hf", "ハフニウム"),
    ("Ta", "タンタル"),
    ("W", "タングステン"),
    ("Re", "レニウム"),
    ("Os", "オスミウム"),
    ("Ir", "イリジウム"),
    ("Pt", "白金"),
    ("Au", "金"),
    ("Hg", "水銀"),
    ("Tl", "タリウム"),
    ("Pb", "鉛"),
    ("Bi", "ビスマス"),
    ("Po", "ポロニウム"),
    ("At", "アスタチン"),
    ("Rn", "ラドン"),
    ("Fr", "フランシウム"),
    ("Ra", "ラジウム"),
    ("Ac", "アクチニウム"),
    ("Th", "トリウム"),
    ("Pa", "プロトアクチニウム"),
    ("U", "ウラン"),
    ("Np", "ネプツニウム"),
    ("Pu", "プルトニウム"),
    ("Am", "アメリシウム"),
    ("Cm", "キュリウム"),
    ("Bk", "バークリウム"),
    ("Cf", "カリホルニウム"),
    ("Es", "アインスタイニウム"),
    ("Fm", "フェルミウム"),
    ("Md", "メンデレビウム"),
    ("No", "ノーベリウム"),
    ("Lr", "ローレンシウム"),
    ("Rf", "ラザホージウム"),
    ("Db", "ドブニウム"),
    ("Sg", "シーボーギウム"),
    ("Bh", "ボーリウム"),
    ("Hs", "ハッシウム"),
    ("Mt", "マイトネリウム"),
    ("Ds", "ダームスタチウム"),
    ("Rg", "レントゲニウム"),
    ("Cn", "コペルニシウム"),
    ("Nh", "ニホニウム"),
    ("Fl", "フレロビウム"),
    ("Mc", "モスコビウム"),
    ("Lv", "リバモリウム"),
    ("Ts", "テネシン"),
    ("Og", "オガネソン"),
  ]
  .iter()
  .cloned()
  .collect()
}

```

## Workflowをつくる
Alfredを開いたら左のアイコンから「Workflows」を選択し、左下の+ボタンを押します。
![スクリーンショット 2020-08-11 16.18.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/b91be4c8-6318-d40d-f98a-c1ddfd204646.png)

「Blank Workflow」を選択します。
![スクリーンショット 2020-08-11 16.34.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/bf263a01-d2cf-1d18-cb8f-adf3ebf437de.png)

新規作成するWorkflowの名前や説明を入力します。
![スクリーンショット 2020-08-11 16.34.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/011e5798-2204-5827-3bf7-41c32897dc08.png)

すると空のWorkflowができますので、キャンバス内を右クリックして表示されるメニューからInputs > Script Filterを選択します。
![スクリーンショット 2020-08-11 16.35.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/cd858946-0484-c113-6c67-13099b94bbf1.png)

Keyword欄にはWorkflowを起動するキーワードを入力します。
ここでは「element」としましたが、5文字以下の短いキーワードが使われることが多いです。
Script欄には実行したいコードを書きます。
ソースコードをコンパイルして得られた実行可能ファイルに引数を渡してあげればOKです。
![スクリーンショット 2020-08-11 16.40.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/15f5d0e6-889f-b059-e0e5-394baad89596.png)

実行可能ファイルはWorkflowごとに作成されるディレクトリに配置しましょう。
右クリックして表示されるメニューから「Open in Finder」を選択するとFinderが開きます。
この時点ではinfo.plistしか存在しないと思います。
このディレクトリに実行可能ファイルを置いてください。
![スクリーンショット 2020-08-11 17.08.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/f1312ea1-378e-92fb-cbf6-64cef2ff35bf.png)


次はクリップボードにコピーするオブジェクトを追加しましょう。
右クリックして表示されるメニューからOutputs > Copy to Clipboardを選択します。
ダイアログが表示されますが、何も変更せずに保存します。
![スクリーンショット 2020-08-11 16.55.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/006ee34a-df30-5135-5c5d-bbfd82fd4ef2.png)

最後に、Script FilterオブジェクトとCopy to Clipboardオブジェクトを結線したら完成です。
![スクリーンショット 2020-08-11 17.00.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334984/af1ab986-da9e-7a8a-a9e4-69350b5f7869.png)

# まとめ
Alfred Workflowsの紹介にはじまり、RustでWorkflowを作る方法を紹介しました。
今回取り上げたソースコードではデータが直書きでしたが、APIを叩けば天気予報Workflowなんかも作れます。
Workflowにアイコンを設定すると途端にオシャレになるのでおすすめです。


# 参考

[Alfred - Productivity App for macOS](https://www.alfredapp.com/)
[alfred - Rust](https://docs.rs/alfred/4.0.2/alfred/)
[Time Zones](https://jhartman.pl/2016/12/14/alfred-3-workflow-time-zone/)
[alfred-emoji](https://github.com/jsumners/alfred-emoji)
[Alfred 4でのWorkflowの作り方とWorkflow Objectのまとめ – Webrandum](https://webrandum.net/alfred4-how-to-create-workflow/)