Current section
Files
Jump to
Current section
Files
lib/jsonnex.ex
defmodule Jsonnex do
@moduledoc """
The Jsonnex library allows you to compile Jsonet files into JSON
right from Elixir.
## Design Decisions
Writing a Jsonnet compiler from scratch is a
large undertaking and not something that I have the time to do, so
instead I decided to use the [Golang reference implementation from
Google](https://github.com/google/go-jsonnet), build a WASM binary
encapsulating that library and then executing that WASM binary via
`Wasmex`.
There are Rust Jsonnet compilers available, but some of
them use `unsafe` in their code and so I did not want to risk crashing
the BEAM and others seem to be unmaintained. Leveraging the Golang
reference implementation seemed like the best way forward.
## Usage
The Jsonnex library comes bundled with the WASM binary for compiling Jsonnet
files, but you can instead use your own version of the WASM binary if you want
by running the included `build.sh` script, copying the output WASM binary to a
directory of your chosing and then overriding the binary path by providing
`wasm_path: "your_new_path"` to the `compile/2` or `compile!/2` functions.
The Jsonnex library provides two main components:
- `Jsonnex.Compiler` - This enables you to compile a Jsonnet snippet into JSON.
- `Jsonnex.Format` - This enables you to format Jsonnet snippets (and also `~JSONNET` sigil calls if you leverage the `Jsonnex.MixFormatter` plugin).
The Jsonnet compiler can take a standalone snippet of Jsonnet code and compile
it into JSON, or you can pass in virtual files to enable `import` statements to
work, or you can even pass in paths to directories to mount to the Wasmex so that
the WASM binary has access to those directories.
## Examples
iex> {:ok, %{"person1" => _, "person2" => _}} =
...> Jsonnex.compile(~S[
...> // A function that returns an object.
...> local Person(name='Alice') = {
...> name: name,
...> welcome: 'Hello ' + name + '!',
...> };
...> {
...> person1: Person(),
...> person2: Person('Bob'),
...> }])
iex> {:ok, "{a: 1 + 2, b: \\"hello\\"}\\n"} =
...> Jsonnex.format(~S[{ a: 1 + 2, b: "hello" }])
iex> {:ok, %{"Manhattan" => _, "Vodka Martini" => _}} =
...> Jsonnex.compile(\"""
...> local martinis = import 'martinis.libsonnet';
...>
...> {
...> 'Vodka Martini': martinis['Vodka Martini'],
...> Manhattan: {
...> ingredients: [
...> { kind: 'Rye', qty: 2.5 },
...> { kind: 'Sweet Red Vermouth', qty: 1 },
...> { kind: 'Angostura', qty: 'dash' },
...> ],
...> garnish: importstr 'garnish.txt',
...> served: 'Straight Up',
...> },
...> }
...> \""",
...> virtual_files: %{
...> "martinis.libsonnet" => \"""
...> {
...> 'Vodka Martini': {
...> ingredients: [
...> { kind: 'Vodka', qty: 2 },
...> { kind: 'Dry White Vermouth', qty: 1 },
...> ],
...> garnish: 'Olive',
...> served: 'Straight Up',
...> },
...> Cosmopolitan: {
...> ingredients: [
...> { kind: 'Vodka', qty: 2 },
...> { kind: 'Triple Sec', qty: 0.5 },
...> { kind: 'Cranberry Juice', qty: 0.75 },
...> { kind: 'Lime Juice', qty: 0.5 },
...> ],
...> garnish: 'Orange Peel',
...> served: 'Straight Up',
...> },
...> }
...> \""",
...> "garnish.txt" => "Maraschino Cherry"
...> }
...> )
"""
@doc false
def wasm_path do
Application.get_env(
:jsonnex,
:wasm_path,
Path.join(:code.priv_dir(:jsonnex), "jsonnex.wasm")
)
end
defdelegate compile(input, opts \\ []), to: Jsonnex.Compiler
defdelegate compile!(input, opts \\ []), to: Jsonnex.Compiler
defdelegate format(input, opts \\ []), to: Jsonnex.Format
defdelegate format!(input, opts \\ []), to: Jsonnex.Format
end