この記事は 広島大学ITエンジニア Advent Calendar 2020の20日目です。
今回の記事は、後で楽をするために結構苦労した話を書いています。
楽したくないですか?
自分は記事を書いた後、新しく記事のファイルの場所を記述したstr
を定数で持たせるようなコードを毎回書いています。このような毎回同じようなコードを書くときは面倒であるため、自動化したいものです。
const ABOUT_CREATED_LOGO: &str = include_str!("./about_created_logo.ja.md");
const ABOUT_CREATED_SITE: &str = include_str!("./about_created_site.ja.md");
...
static ARTICLE_MAP: phf::Map<&str, &str> = phf_map! {
"about_created_logo" => ABOUT_CREATED_LOGO,
"about_created_site" => ABOUT_CREATED_SITE,
// ...
};
自動化
自動化するにあたって、記事のあるディレクトリからすべてのファイル名をとりループさせればできるだろうという甘い考えを持っていました。
ディレクトリ内のファイル名取得はstd::fs::read_dir(SOURCE_DIR)
を用いれば、簡単に実装できます。しかしその後の処理に少し問題がありました。
問題点
const
やstatic
を使用したいため、ループでする方法がわからないinclude_str!
はコンパイル時に実行するため、引数に変数を用いれない
以上の問題点が浮かび、どうしたらいいか悩みました。macroを用いてもできそうにないので、調べていたところbuild.rs
を見つけました。
build.rs
build.rsはクレートをビルドする前に実行するビルドスクリプトです。主にRust以外の言語をコンパイルしたいみたいなときに使用するらしいのですが、.rs
であるためもちろんRustのコードを書いてもいいのです。
実装
ということでビルドスクリプトの部分でrustのコードをファイルに記述し、include
マクロでコードを導入します。クレートのビルド時には、include_str
マクロの引数等はすでに文字列に代わっているという方法でコンパイルエラーを回避します。
pub static ARTICLE_MAP: phf::Map<&str, &str> = include!("./article_map.rs");
この後build.rsにコードを書いていったのですが、たくさんのエラーに悩まされました。
クレート内は読み込まない
ビルドスクリプトではクレートのビルド前に実行するため当たり前ですが、クレート内のコードを使用できません。
そのため外部クレートに必要な構造体を移行しました。
外部クレートを読み込んでくれない
外部クレートに構造体を移したためCargo.toml
の[dependencies]
に追加したのですが、上手く外部クレートを読み込んでくれません。
error[E0433]: failed to resolve: use of undeclared crate or module `shared`
--> client/build.rs:1:5
|
1 | use shared::models::meta::Meta;
| ^^^^^^ use of undeclared crate or module `shared`
ビルドスクリプトの場合[build-dependencies]
に記述する必要がありました。
[build-dependencies]
shared = { path = "../shared" }
constはconst関数以外許さない
現在の構造体の新規作成をString
に変えるtraitか何かが欲しいなと思いつつ地道にinclude!
のファイルの部分を、時間をかけて書いて完成かと思ったら、問題が起こりました。
pub static ARTICLE_META_MAP: phf::Map<&str, &Meta> = include!("../../article_meta_map.rs");
記事のメタ情報の部分は構造体を用いていたのですが、どうやらconst
等はconst関数しか使えないということでした。つまりString::from()
すら使えません。となるとどうやってString
に変換すればいいのかわからず、Meta
構造体の構造を変える必要がありました。
pub struct Meta {
pub seed: String,
pub title: Option<String>,
// ...
String
はすべて&str
に変えるなどしました。結構フィールドの数が多かったり違う型とか使っていたため、これもまた時間がかかりました。
pub struct Meta<'a> {
pub seed: &'a str,
pub title: Option<&'a str>,
// ...
完成
これらとちょっとしたエラーに対処し何とか自動化することができました。
終わりに
今後sitemap.xml
を追加する際にもbuild.rsを用いることができるため、Rustを書く時の柔軟性が向上しました。
しかしリアルタイムで記事を生成したり、記事を書く度に何行か追加するということを面倒に思わなければ、今回のコードは必要なかったのかもしれません。逆にとても時間を使ってしまいました。
最後までお読み下さり、ありがとうございました。