🔧

nixファイルの位置情報取得する何か(__curPos)を用いたリッチなwarn

2023/09/07に公開

手元のnixバージョンは 2.18.0pre20230828_736b9ce です

前記事 https://zenn.dev/ruicc/articles/33e5f9c5103e6a ではbuiltins.unsafeGetAttrPosを用いることでファイルの位置情報を取得を試みたわけですが、これは正確にはsetの特定のattributeの位置情報を取得するというものでした。まあ名前からしてそうなんですが。
さてこれを用いてユーザーがwarnを呼び出した位置情報を取得したいわけですが、まああまり綺麗な形にはならなそうです(いや別にこれでも良いんですが)。
nixのtest周りを眺めていたところ、__curPosという何かを見つけました。これはbuiltins.unsafeGetAttrPosとは異なり、位置情報を直に取得出来るものの様です。これを用いてlib.warnよりリッチなwarn関数を作ってみます。

test.nix
let
  pkgs = import <nixpkgs> {};
  warn = info: val:
    if info?pos && info?message
    then pkgs.lib.warn "${info.message} at ${baseNameOf info.pos.file}:${toString info.pos.line}:${toString info.pos.column}" val
    else pkgs.lib.warn info val;
in
  warn { pos = __curPos; message = "Hello rich warning!"; } 42

実行してみます

$ nix eval --file test.nix
trace: warning: Hello rich warning! at test.nix:8:16
42

良いですね。warningに位置情報をつけることが出来ました。
いくらか試してみましたが、__curPos は遅延評価を無視してそれ自身が置かれた場所の情報を取得することができる様です。このwarnは呼ぶ際に追加情報が必要ですが、悪くないのではないでしょうか。というか恐らくnix expressionの機能的にこれ以上簡潔にするのは難しい気がします。もちろん将来的に便利関数が追加されることもあるかもしれないですが。

追記

いややっぱりunsafeGetAttrPos使った方が良さそうですね。仕組みが一見わかりづらいですが。

test.nix
let
  pkgs = import <nixpkgs> {};
  warn = info: val:
    if info?message
    then
      let pos = builtins.unsafeGetAttrPos "message" info;
      in pkgs.lib.warn "${info.message} at ${baseNameOf pos.file}:${toString pos.line}:${toString pos.column}" val
    else pkgs.lib.warn info val;
in
  warn { message = "Hello rich warning!"; } 42

Discussion