flutterのfreezedを外部ライブラリにも使いたい
そう思ったことはありませんか?私はあります。
こんなコードを想像してみましょう。
import 'package:some_3rd_party_lib/some_3rd_party_lib.dart';
void main() {
final res = somethingOf3rdPartyLib();
println(res); // output Instance of `3rdPartyLibClass`
}
外部のライブラリはおそらくpure dartなclassで実装されたものをレスポンスとして返してくれます。
しかし、このレスポンスをdebug printしてもfreezedの様にclassの中身をいい感じに出力してはくれません。
もちろん外部のライブラリのclassを確認して通常通りfreezed用のボイラープレートを書けばそれで済む話ではあります。
part of 'extended_3rd_party_lib.dart';
abstract class Freezed3rdPartyLibClass implements _$Freezed3rdPartyLibClass {
const Freezed3rdPartyLibClass._();
factory Freezed3rdPartyLibClass({
String id,
int number,
String description,
}) = _Freezed3rdPartyLibClass;
factory Freezed3rdPartyLibClass._$From(Todo self) => Freezed3rdPartyLibClass(
id: self.id,
number: self.number,
description: self.description,
);
}
extensionも作っておけばフィールドをわざわざ指定しなくても済むのでいいかもしれません。
extension Extended3rdPartyLibClass on 3rdPartyLibClass {
FreezedTodo freeze() => Freezed3rdPartyLibClass._$From(this);
}
しかしこの方法だと外部のライブラリが更新されてclassの構造が変わった場合に追従する手間が発生します。
そこでこれを自動で行ってくれるライブラリを作りました。
freezifyです。
使い方は至って簡単で、適当なファイルにextensionを定義してfreezifyのannotationを指定します。
import 'package:some_3rd_party_lib/some_3rd_party_lib.dart';
// 必須
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:freezify_annotation/freezify_annotation.dart';
part 'extended_3rd_party_lib.freezed.dart';
part "extended_3rd_party_lib.freezify.dart";
extension ExtendedTodo on Todo {
FreezedTodo freeze() => FreezedTodo._$From(this);
}
これだけです。
あとはfreezedでお馴染みのbuild_runnerを走らせてあげればfeezifyから自動でfreezedのボイラープレートが生成され、それを元にfreezedのコード生成を行ってくれます。
やってることは至ってシンプル、を通り越して雑で、code_genのタイミングで以下のようなテンプレートでfreezedのボイラープレートを生成しているだけです。
abstract class Freezed${_extendedType} implements _\$Freezed${_extendedType} {
const Freezed${_extendedType}._();
factory Freezed${_extendedType}({
${_fields}
}) = _Freezed${_extendedType};
factory Freezed${_extendedType}._\$From(${_extendedType} self) => Freezed${_extendedType}(
${_fieldArgs});
}
builders:
freezify:
import: "package:freezify/builder.dart"
builder_factories: ["freezify"]
build_extensions: { ".dart": [".freezify.dart"] }
auto_apply: dependents
build_to: source
runs_before: ["freezed:freezed", "json_serializable|json_serializable"]
builderのコードは生成するコードの依存関係を指定できるのでfreezify -> freezedの順でコード生成することで今回の仕組みを実現しています(freezedもjson_serializableの前に実行するように設定されています)
便利ライブラリとして紹介しましたが、現時点では個人用に作ったのでテストもドキュメントもなく、そろそろリリースされるであろうnull-safety版での動作確認も出来ていません(自分の環境では問題なく動いていますが)。
これを公開することで「freezedだけでもできるよ」とか「こんなライブラリもあるよ」みたいな反応があるといいなと思い書きました。
コード生成のツールも一度作ってみたかったので挑戦してみてよかったです。
眠いので以上です。
Discussion