Tuzuru開発の知見

By ainame / Published on 7 September 2025

前の記事の続きで、SwiftでCLIを開発していた中で実装していて面白かった箇所を最後に紹介する。

swift-markdown

swiftlang/swift-markdownはかなり便利でよく出来ていた。ASTをVisitorパターンで操作できて、HTMLへの変換もVisitorパターンで実装されている https://github.com/swiftlang/swift-markdown/blob/c281baa59e2b0d3ddc72848741217af811f66d39/Sources/Markdown/Walker/Walkers/HTMLFormatter.swift

これのためにSwift使ってよかったというぐらい良い感じだった。唯一の不満点としては、標準のHTMLFormatterではリストの要素がpタグでラップされてしまうので正規表現で後から消す必要があった点。

https://github.com/swiftlang/swift-markdown/issues/197#issuecomment-2351568887

Swift Testing Traits

単体テストでGit依存の処理をテストするためにswift-testingのTraitを活用してFixtureを生成した。Swift Testingは標準で並列に実行されるので、Fixtureのセットアップも丁寧にやらないといけないけど、@TaskLocal というマクロで Task ごとのFixtureデータをラップしたオブジェクトを安全にテストケースから参照できた。

https://github.com/ainame/tuzuru/blob/4b186a46e9c9a6a5726237753b9a1370c48a9af9/Tests/TuzuruLibTests/Traits/GitRepositoryFixtureTrait.swift

apple/container

apple/containerでローカルでLinux上の動作環境を用意してみた https://github.com/ainame/tuzuru/blob/4b186a46e9c9a6a5726237753b9a1370c48a9af9/scripts/test-linux-static.sh

Dockerファイルで普通に動くので、とりあえずこれでいいじゃんって気持ちになった。

組み込みHTTPサーバー

プレビュー用のHTTPサーバーは、Linuxでも動作させる+SwiftNIOのような既存のような巨大な依存を避けるために、システムコールを使ってClaude Codeに実装してもらって勉強になった(socket, bind, listen, acceptなどなど昔読んだ本の通りになった)。

https://github.com/ainame/tuzuru/blob/4b186a46e9c9a6a5726237753b9a1370c48a9af9/Sources/ToyHttpServer/ToyHttpServer.swift

FileManager

FileManagerを扱うときの知見を得た。

https://github.com/ainame/tuzuru/blob/4b186a46e9c9a6a5726237753b9a1370c48a9af9/Sources/TuzuruLib/Utils/FileManagerWrapper.swift

以下のような理由からラッパークラスを作ったらいい感じになった。

  • swift-systemのFilePathをcurrency typeとして利用したい
  • FileManager はSendableではない
  • 一方でほとんどのインスタンスメソッド自体はスレッドセーフらしいの機能を絞れば @unchecked Sendableには出来そう?
  • FileManager.currentDirectoryPath はインスタンス単位ではなく、プロセス単位の値らしいので、並列なテストでは利用できない
  • AIが不必要に FileManager.default を直接参照してくるので、機能を絞ったラッパーのクラスを使ってDIするように指示すること治安が保てた

新たに作った FileManagerWrapper@unchecked Sendable として運用して、TaskGroupで並列化するときに利用している。

swift-system と swift-subprocess

Foundation.URL よりも使いやすい FilePath が使いたくて apple/swift-sytem を採用して、Gitを呼ぶためにswiftlang/swift-subprocessも採用したが、swift-subprocessmacOS上ではXcodeのSDKの System framework (swift-systemと同等だけど別物として存在) がimport出来る時には利用していて、Linux前提で swift-systemFilePath を先に使っていた自分は swift-subprocessFilePath を渡せずに混乱していた。

Appleの中の人々が議論しているissueがあって、みんな意見が分かれていそうなので早く終息してほしいと願っている。 https://github.com/swiftlang/swift-subprocess/issues/141

swift-subprocessのやり方に寄り添った typealias を書いて解決。 https://github.com/ainame/tuzuru/blob/85317a53e71acfed7daf30cace3797a23d699b8b/Sources/TuzuruLib/SystemPackageCompat.swift

まとめ

Swiftの普段使わない要素を試せて面白かった。 Claude Codeなどのおかげで趣味開発は捗るようになって楽しい。