[小ネタ] Pklをビルド時に実行する[Rust]

2024.02.07

Introduction

Pkl(ぴっくる)はAppleが開発した、
OSSのconfig file生成用プログラミング言語です。
条件式やループなどの基本構文に加え、クラス・継承・抽象化などの
プログラミング言語機能も使えます。
pklファイルからJSON形式やYAML形式などの各種設定ファイルが出力可能で、
CLIツールや各言語のライブラリやプラグインとして使えます。

※Pklの詳細については公式紹介記事をご確認ください。

現状想定されているユースケースとしては、
下記のものがあります。

Generating Static Configuration(静的構成ファイル生成)

設定ファイルのgenerate機能です。
Pklで記述したコードをJSON、YAML、XML、プロティリスト形式で出力できます。

Application Runtime Configuration(アプリランタイム構成)

Pklをライブラリとして追加し、プログラム内で使用する機能です。
2024年2月時点ではJVM runtime(Java,Kotlin),Swift,Goに対応した
ライブラリが提供されています。
これらは今後追加されるみたいです。

今回はPklをインストールして、
Rustでビルド時にJSONファイルを生成してみます。

Environment

  • MacBook Pro (13-inch, M1, 2020)
  • OS : MacOS 13.5.2
  • Rust : 1.75.0

Setup

各環境にあわせてPklをインストールします。
自分の環境では下記のようにインストールしました。

% curl -L -o pkl https://github.com/apple/pkl/releases/download/0.25.1/pkl-linux-aarch64

% chmod +x pkl

PklをPATHに追加してコマンドを実行してみます。

% pkl --version
Pkl 0.25.1 (macOS 14.1, native)

次にCargoでRustプロジェクトを作成します。

% cargo new pkl-example

あとで確認用に使うのでserdeを追加。

% cd pkl-example
% cargo add serde serde_json

プロジェクトのルートにintro.pklを下記内容で作成。

name = "Pkl: Configure your Systems in New Ways"
attendants = 100
isInteractive = true
amountLearned = 13.37

JSONで標準出力してみる。

% pkl eval -f json ./intro.pkl
{
  "name": "Pkl: Configure your Systems in New Ways",
  "attendants": 100,
  "isInteractive": true,
  "amountLearned": 13.37
}

Cargo.tomlにbuild.rsを指定しましょう。

[package]
・
・
build = "build.rs"

プロジェクトのルートにbuild.rsを作成します。
雑にpklのパスを設定してJSON出力コマンドを実行するだけです。

// build.rs
use std::process::Command;
use std::io;
use std::env;

fn main() -> io::Result<()> {
    println!("build.rs start");
    // 現在のPATHを取得してPATHにpkl追加&環境変数設定
    let mut path = env::var_os("PATH").unwrap_or_default();
    path.push(":");
    path.push("<your pkl path>");
    env::set_var("PATH", &path);

    //pklコマンド実行(intro.json出力)
    let output = Command::new("pkl")
        .arg("eval")
        .arg("-o")
        .arg("./intro.json")
        .arg("-f")
        .arg("json")
        .arg("./intro.pkl")
        .output()?;

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

    if !output.status.success() {
        panic!("Failed to execute pkl command");
    }

    Ok(())
}

src/main.rsでは生成されたjsonファイルにアクセスしてます。

use serde_json::Value;
use std::fs;

fn main() {
    let data = fs::read_to_string("intro.json").unwrap();
    let v: Value = serde_json::from_str(&data).unwrap();

    // JSONデータにアクセスする
    println!("name: {}",  v.get("name").unwrap());
    println!("attendants: {}",  v.get("attendants").unwrap());
    println!("isInteractive: {}",  v.get("isInteractive").unwrap());
    println!("amountLearned: {}",  v.get("amountLearned").unwrap());
}

実行してみます。
jsonが出力されてアクセスできてます。

% cargo build -vv
% cargo run 
・・・
name: "Pkl: Configure your Systems in New Ways"
attendants: 100
isInteractive: true
amountLearned: 13.37

Summary

build.rsからシンプルにpklコマンド実行してみました。
Rust用crateはそのうちでてくると思われるので
プログラム内からアクセスするのはもう少し待ちましょう。

References