Rustでタイポチェックするクレート作った話〜crates.ioでの公開まで〜

# はじめに
こんにちは。細々とプログラミングをしている[sotanengel](https://x.com/sota_eng_prog)です。

Rustの勉強を兼ねて、
- 英単語のタイポを検出するクレートの作成
- 作ったクレートをcrates.ioで公開

までやったので公開までの流れを本投稿で記載します。

個人的にクレート公開することで勉強のモチベ上がったので、もし興味ある人はチャレンジしてみてください！
具体のコードが気になる方は、以下にリンクを貼っておくのでまずそっちをご覧くださいませ。

https://github.com/sotanengel/typo_checker

https://crates.io/crates/typo_checker/1.0.0


以下、作成したクレートの使い方・やったことをまとめていきます👀

# 使い方
## 1. クレートをインストールする
以下のコマンドでクレートをインストール。

```bash
cargo add typo_checker
```

## 2. Rustのコード内部で実行する
クレート内の`check_a_word`関数に確認したい単語を入れる。

```rust
use typo_checker;

fn main() {
    let a = ;
    let typo_chec_result = typo_checker::check_a_word(a.to_string(), Some(2), 5, None);

    println!("typo_chec_result: {:?}", typo_chec_result);
}

```
上記のコードであれば、以下の出力が得られます。
出力はTypoCheckResult型という独自の型で出力されます。
もしも正しい単語であれば`match_word`部分に値が挿入されますが、一致するものがない場合は`similar_word_list`フィールドに類似した単語が格納されます。
具体的なフィールドの説明については[ドキュメント](https://docs.rs/typo_checker/1.0.0/typo_checker/)をご覧ください。

```bash
typo_chec_result: TypoCheckResult { match_word: None, similar_word_list: Some([SimilarWord { spelling: "type", levenshtein_length: 1, typo_type: MissingCharacters { character: 'e', position: Tail } }, SimilarWord { spelling: "gyp", levenshtein_length: 1, typo_type: CloseKeyboardPlacement }, SimilarWord { spelling: "tup", levenshtein_length: 1, typo_type: CloseKeyboardPlacement }, SimilarWord { spelling: "tap", levenshtein_length: 1, typo_type: UndefinedType }, SimilarWord { spelling: "tip", levenshtein_length: 1, typo_type: UndefinedType }]) }
```

# やったこと
## 1. cargoでクレート作成用のコマンドを実行する
一番最初に`cargo`使ってプロジェクト作ろうとしたんですが、そもそもクレート作成用の引数あるの知らんかったのでちょっと手戻り。。。
以下のように`--lib`をつけるとlib作成用のフォルダ群が作成されます([公式ドキュメント]())。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1758960/f8e7e109-e774-0536-db50-16a59ec509f3.png)


```bash
 cargo new typo_checker --lib
```
## 2. ミニマムのコードを書いてみる
単語同士の「似ているかどうか」の指標はレーベンシュタイン距離で判断すると決めていたので、まずは正しい英単語の情報の格納とレーベンシュタイン距離を計算する実装を行いました。

※ 1. [レーベンシュタイン距離(wiki)](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2)
※ 2. 正しい英単語はEJDictさんがパブリックドメインで公開されている『English-Japanese Dictionary "ejdict-hand"([GitHub](https://github.com/kujirahand/EJDict?tab=readme-ov-file))』を使用しました。ありがとうございます🙇

## 3. Rustのドキュメントを書いてみる
外部に公開するということは「大勢の人が見ても仕様を理解できないといけない」ということと同義なので、Docs.rsでわかりやすくなるようにコメントなどの追加を行いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1758960/f4f37ab9-c8cd-7df6-6fc9-24656f3d5895.png)

## 4. crate.ioのアカウントを作成する
ちょっと古い記事ですが、以下の記事を参考にして設定を行いました。
結構網羅的にやるべきことが書かれていて、いい記事でした👀

https://qiita.com/tasshi/items/169c372b5230e0bd8b14

## 5. Cargo.tomlに必要事項を書き込み、初投稿する
crate.ioに投稿する際にはCargo.tomlの`package`に最低限書かなければいけないメタデータがあるので、それらを追記しコマンドを実行しました。
```bash
cargo publish
```

※ 1. [Cargo.tomlに記載できるメタデータ一覧(公式)](https://doc.rust-lang.org/cargo/reference/manifest.html)


## 6. 機能追加する
最初に作成したものはミニマムだった&投稿するとモチベが湧いてきたので、機能をどんどん追加していきました。初投稿時のやつはベータ版だったので、この時点でテストなども追加。

## 7. バージョンアップする
Cargo.tomlのversionフィールドを記載しなおして、正式版を投稿！！


# 頑張った点
## 1. 自分なりのロジックで実装した
今回作ったクレートは将来的に「英語の文章中のタイポを見つけ、類似単語を提案する」ことを目標にしていたので、できるだけ処理を早くしたいと思っていました。
そのため以下のようなロジックを組んで、その通りに動くものが作れたのでよかったです。

【ロジック】
1. チェックする単語の文字数と同じ文字数の単語を比較(完全一致すれば処理を終了)
2. 完全一致しなければレーベンシュタイン距離を計算し、チェックする単語の文字数に近い文字数の英単語のレーベンシュタイン距離を計算する


## 2. タイポの種類を自分で定義した
一番最初に実装した時は「レーベンシュタイン距離が近い順にソートして結果を出力するだけ」だったのですが、思ったよりも同じレーベンシュタイン距離の単語が多かったので出力が味気なくなってしまいました。。。。
```bash
typo_chec_result: TypoCheckResult { match_word: None, similar_word_list: 
Some([SimilarWord { spelling: "hypo", levenshtein_length: 1 }, SimilarWord { 〜〜〜〜〜〜
```

そこで「どんな種類のタイポか」を定義することで、タイポ文字があった場合にユーザーにわかりやすく指摘できるようにしました([docs.rs](https://docs.rs/typo_checker/1.0.0/typo_checker/enum.TypoType.html))。
```bash
typo_chec_result: TypoCheckResult { match_word: None, similar_word_list:
Some([SimilarWord { spelling: "type", levenshtein_length: 1, typo_type: MissingCharacters { character: 'e', position: Tail } }, SimilarWord { 〜〜〜
```

# 悩んだところ
## 1. docs.rsに即した書き方
一応、[公式ドキュメント](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html)があるっちゃあるんですが「構造体はこう書く」みたいなものが見つけられなかったので、途中まで表記が安定しませんでした。
最終的にダウンロード数も多く、昔からある[imageクレート](https://crates.io/crates/image)のdocs.rsと実際のコードを見比べながら真似っこして書いていきました。

## 2. バージョンの定義の仕方
[セマンティックバージョニング](https://semver.org/lang/ja/)に基本的に配慮する必要があるらしいんですが、最初はイマイチよく分からず更新したいときに適当にバージョン上げてしまってました・・・・
個人的には以下のサイトの説明がしっくりきました。

https://www.tohoho-web.com/ex/semver.html

## 3. コンパイル時間が遅い・・・・・
Rustだから&今回の実装では英単語を結構な数ハードコーディングしちゃってるので仕方がないのかなと思うんですが、実装できる機能に対して10分もコンパイルに時間かかるのもちょっとなと・・・・・
(誰か詳しい人いたら[issue](https://github.com/sotanengel/typo_checker/issues)からアドバイスいただけると嬉しいです🙇)

# さいごに
せっかく作ったクレートなので、もしも要望だったり改善ポイントがあれば以下のissueよりコメントいただけると助かります！！！！(スキマ時間に対応するかも...)。

https://github.com/sotanengel/typo_checker/issues