🧪

ElixirからTOML v1.0.0のファイルを扱えるライブラリを調べる

2021/09/22に公開

はじめに

2021年の1月にTOML v1.0.0がリリースされたようです。
https://github.com/toml-lang/toml/releases/tag/1.0.0

v0.5.0からv1.0.0-rc.1、v1.0.0-rc.2、v1.0.0-rc.3を経ての正式バージョンのリリースです。
この記事では、ElixirでTOML v1.0.0を使えるライブラリを調べてみました。

v0.5.0とv1.0.0の違い

下記の和訳されたドキュメントを比較しましたが、自分が見つけられた記述面の違いは下記の1つだけでした。

後ほど改めて英語版のv1.0.0のドキュメントと比較してみます。

https://toml.io/ja/v0.5.0
https://toml.io/ja/v1.0.0-rc.2

Elixirライブラリの対応状況

TOMLは各バージョンごとに対応するプログラミング言語のライブラリを公式GitHubのwikiページに掲載しています。
https://github.com/toml-lang/toml/wiki#implementations

以下の表は、上記のwikiページからElixirから呼べるライブラリを抜き出したものです。

ライブラリ名 対応TOMLバージョン
toml v0.5.0
tomerl v1.0.0

各ライブラリの動きを試す

先述の表にあるライブラリで、v1.0.0とv0.5.0の仕様で記述されたTOMLファイルをパースできるか比較してみます。

まず、下記のファイルはTOML公式GitHubにも記載されているサンプルデータです。
v0.5.0でも解釈できる記述で書かれている(はず)

sample.toml
# This is a TOML document.

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # First class dates

[database]
server = "192.168.1.1"
ports = [ 8000, 8001, 8002 ]
connection_max = 5000
enabled = true

[servers]

  # Indentation (tabs and/or spaces) is allowed but not required
  [servers.alpha]
  ip = "10.0.0.1"
  dc = "eqdc10"

  [servers.beta]
  ip = "10.0.0.2"
  dc = "eqdc10"

[clients]
data = [ ["gamma", "delta"], [1, 2] ]

# Line breaks are OK when inside arrays
hosts = [
  "alpha",
  "omega"
]

こちらは、v1.0.0-rc2のドキュメントに掲載されている"配列中に複数種類のデータ型を含んだ配列"を持つTOMLファイルになります。

v1.0.0.toml
integers = [ 1, 2, 3 ]
colors = [ "red", "yellow", "green" ]
nested_array_of_int = [ [ 1, 2 ], [3, 4, 5] ]
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
string_array = [ "all", 'strings', """are the same""", '''type''' ]

# 異なる型を混ぜて使うこともできます。
numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
contributors = [
  "Foo Bar <foo@example.com>",
  { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" }
]

TOML(Elixir)

$ iex -S mix

# v0.5.0対応のファイルはパース成功
iex(1)> File.read!("sample.toml") |> Toml.decode!
%{
  "clients" => %{
    "data" => [["gamma", "delta"], [1, 2]],
    "hosts" => ["alpha", "omega"]
  },
  "database" => %{
    "connection_max" => 5000,
    "enabled" => true,
    "ports" => [8000, 8001, 8002],
    "server" => "192.168.1.1"
  },
  "owner" => %{
    "dob" => ~U[1979-05-26 23:32:00Z],
    "name" => "Tom Preston-Werner"
  },
  "servers" => %{
    "alpha" => %{"dc" => "eqdc10", "ip" => "10.0.0.1"},
    "beta" => %{"dc" => "eqdc10", "ip" => "10.0.0.2"}
  },
  "title" => "TOML Example"
}

# v1.0.0の記述を書いたファイルはパース失敗(バージョン未対応なので想定通り)
iex(2)> File.read!("v1.0.0.toml") |> Toml.decode!
** (EXIT from #PID<0.210.0>) shell process exited with reason: an exception was raised:
    ** (ArgumentError) argument error
        (stdlib 3.15.2) :io.put_chars(:standard_io, :unicode, [[<<27, 91, 51, 49, 109, 42, 42, 32, 40, 84, 111, 109, 108, 46, 69, 114, 114, 111, 114, 41, 32, 123, 58, 105, 110, 118, 97, 108, 105, 100, 95, 97, 114, 114, 97, 121, 44, 32, 123, 58, 101, 120, 112, 101, 99, 116, 101, 100, ...>>], 10, "\e[31m    (toml 0.6.2) lib/decoder.ex:50: Toml.Decoder.decode!/2\n\e[0m"])
        (iex 1.12.2) lib/iex/evaluator.ex:266: IEx.Evaluator.eval/3
        (iex 1.12.2) lib/iex/evaluator.ex:157: IEx.Evaluator.loop/1
        (iex 1.12.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/4
        (stdlib 3.15.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

tomerl

$ iex -S mix

# v0.5.0対応のファイルはパース成功
iex(1)> File.read!("sample.toml") |> :tomerl.parse
{:ok,
 %{
   "clients" => %{
     "data" => [["gamma", "delta"], [1, 2]],
     "hosts" => ["alpha", "omega"]
   },
   "database" => %{
     "connection_max" => 5000,
     "enabled" => true,
     "ports" => [8000, 8001, 8002],
     "server" => "192.168.1.1"
   },
   "owner" => %{
     "dob" => {:datetime_offset, {:date, 1979, 5, 27}, {:time, 7, 32, 0}, -480},
     "name" => "Tom Preston-Werner"
   },
   "servers" => %{
     "alpha" => %{"dc" => "eqdc10", "ip" => "10.0.0.1"},
     "beta" => %{"dc" => "eqdc10", "ip" => "10.0.0.2"}
   },
   "title" => "TOML Example"
 }}
 
 # v1.0.0の記述を書いたファイルもパース成功
iex(2)> File.read!("v1.0.0.toml") |> :tomerl.parse
{:ok,
 %{
   "colors" => ["red", "yellow", "green"],
   "contributors" => [
     "Foo Bar <foo@example.com>",
     %{
       "email" => "bazqux@example.com",
       "name" => "Baz Qux",
       "url" => "https://example.com/bazqux"
     }
   ],
   "integers" => [1, 2, 3],
   "nested_array_of_int" => [[1, 2], [3, 4, 5]],
   "nested_mixed_array" => [[1, 2], ["a", "b", "c"]],
   "numbers" => [0.1, 0.2, 0.5, 1, 2, 5],
   "string_array" => ["all", "strings", "are the same", "type"]
 }}

まとめ

  • ElixirでTOML v1.0.0を使うにはtomerlを使う
  • TOML v0.5.0でも良いなら、toml(Elixir)の選択肢もある

Discussion