Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 53 additions & 22 deletions lib/numscriptex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ defmodule Numscriptex do
alias Numscriptex.Utilities

require AssetsManager
require Logger

@type check_log() :: CheckLog.t()

Expand Down Expand Up @@ -52,6 +51,33 @@ defmodule Numscriptex do

AssetsManager.ensure_wasm_binary_is_valid()

@doc """
`version/0` simply shows a map with both Numscript-WASM and NumscriptEx versions.

Ex:

```elixir
iex> Numscriptex.version()
%{numscriptex: "v0.1.0", numscript_wasm: "v0.0.2"}
```
"""
@spec version() :: %{numscriptex: binary(), numscript_wasm: binary()}
def version do
numscriptex_version =
:numscriptex
|> Application.spec(:vsn)
|> to_string()
|> then(fn vsn -> "v#{vsn}" end)

case execute_command(:version) do
{:ok, numscript_wasm_version} ->
Map.put(numscript_wasm_version, :numscriptex, numscriptex_version)

{:error, _reason} ->
%{numscript_wasm: "unknown", numscriptex: numscriptex_version}
end
end

@doc """
To use `check/1` you just need to pass your numscript as its argument.
Ex:
Expand Down Expand Up @@ -136,6 +162,8 @@ defmodule Numscriptex do
defp maybe_put_final_balance({:error, _reason} = error, _initial_balance),
do: error

defp execute_command(input \\ "", operation)

defp execute_command(input, operation) do
{:ok, stdout_pipe} = Wasmex.Pipe.new()
{:ok, stdin_pipe} = Wasmex.Pipe.new()
Expand All @@ -157,11 +185,11 @@ defmodule Numscriptex do
{:ok, pid} <- Wasmex.start_link(%{bytes: binary, wasi: wasi}),
{{:ok, _}, _pid} <- {Wasmex.call_function(pid, :_start, []), pid} do
GenServer.stop(pid, :normal)
process(pid, stdout_pipe, stderr_pipe)
process(pid, stdout_pipe, stderr_pipe, operation)
else
{{:error, _reason}, pid} ->
GenServer.stop(pid)
process(pid, stdout_pipe, stderr_pipe)
process(pid, stdout_pipe, stderr_pipe, operation)

{:error, reason} when is_atom(reason) ->
{:error, %{reason: handle_posix_errors(reason)}}
Expand All @@ -171,26 +199,18 @@ defmodule Numscriptex do
end
end

defp process(pid, stdout_pipe, stderr_pipe) when is_pid(pid) do
defp process(pid, stdout_pipe, stderr_pipe, operation) when is_pid(pid) do
Wasmex.Pipe.seek(stdout_pipe, 0)
stdout = Wasmex.Pipe.read(stdout_pipe)

Wasmex.Pipe.seek(stderr_pipe, 0)
error = Wasmex.Pipe.read(stderr_pipe)

case JSON.decode(stdout) do
{:ok, _content} = result ->
result
|> handle_process()
|> maybe_put_stderr(error)
|> handle_errors()

{:error, _reason} ->
{:error, stdout}
|> handle_process()
|> maybe_put_stderr(error)
|> handle_errors()
end
stdout
|> maybe_decode_json(operation)
|> handle_operation_result(operation)
|> maybe_put_stderr(error)
|> handle_errors()
end

defp handle_posix_errors(reason) do
Expand All @@ -217,27 +237,38 @@ defmodule Numscriptex do

defp maybe_put_stderr(data, _stderr), do: data

defp handle_process({:ok, %{"valid" => valid?} = result}) when is_boolean(valid?) and valid? do
defp maybe_decode_json(result, :version), do: result
defp maybe_decode_json(result, _operation), do: JSON.decode(result)

defp handle_operation_result(result, operation)

defp handle_operation_result(result, :version) when is_binary(result) do
{:ok, %{numscript_wasm: String.trim(result)}}
end

defp handle_operation_result({:ok, %{"valid" => valid?} = result}, :check)
when is_boolean(valid?) and valid? do
normalized_result = Map.delete(result, "valid")

has_details? = not Enum.empty?(normalized_result)

if has_details?, do: {:ok, normalized_result}, else: :ok
end

defp handle_process({:ok, %{"valid" => valid?, "errors" => _err} = result})
defp handle_operation_result({:ok, %{"valid" => valid?, "errors" => _err} = result}, :check)
when is_boolean(valid?) and not valid? do
{:error, %{reason: Map.delete(result, "valid")}}
end

defp handle_process({:ok, %{"postings" => postings} = result}) do
defp handle_operation_result({:ok, %{"postings" => postings} = result}, :run) do
if Enum.empty?(postings),
do: {:error, %{reason: :invalid_input}},
else: {:ok, result}
end

defp handle_process({:error, reason}), do: {:error, %{reason: reason}}
defp handle_process({:ok, _data} = result), do: result
defp handle_operation_result({:error, reason}, _operation), do: {:error, %{reason: reason}}
defp handle_operation_result({:ok, _data} = result, _operation), do: result
defp handle_operation_result(result, _operation), do: result

defp handle_errors({:ok, _} = result), do: result

Expand Down
10 changes: 6 additions & 4 deletions lib/numscriptex/assets_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ defmodule Numscriptex.AssetsManager do
library have all it needs (i.e. a WASM binary file that is used to check and run Numscripts)
to run correctly at compile time.
"""
require Logger

@release_version Application.compile_env(:numscriptex, :version, "0.0.2")
Module.register_attribute(__MODULE__, :numscript_wasm_version, persist: true)
@numscript_wasm_version Application.compile_env(:numscriptex, :version, "0.0.2")
@retries Application.compile_env(:numscriptex, :retries, 3)
@numscript_checksums_url "https://github.com/PagoPlus/numscript-wasm/releases/download/v#{@release_version}/numscript_checksums.txt"
@numscript_wasm_url "https://github.com/PagoPlus/numscript-wasm/releases/download/v#{@release_version}/numscript.wasm"
@numscript_checksums_url "https://github.com/PagoPlus/numscript-wasm/releases/download/v#{@numscript_wasm_version}/numscript_checksums.txt"
@numscript_wasm_url "https://github.com/PagoPlus/numscript-wasm/releases/download/v#{@numscript_wasm_version}/numscript.wasm"
@binary_path :numscriptex
|> :code.priv_dir()
|> Path.join("numscript.wasm")
|> to_charlist()

defmacro ensure_wasm_binary_is_valid do
quote do
require Logger

File.mkdir_p!(:code.priv_dir(:numscriptex))

if File.exists?(unquote(@binary_path)) do
Expand Down
40 changes: 39 additions & 1 deletion test/numscriptex_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule NumscriptexTest do
use ExUnit.Case
use ExUnit.Case, async: false

alias Numscriptex.AssetsManager
alias Numscriptex.CheckLog

doctest Numscriptex
Expand Down Expand Up @@ -815,4 +816,41 @@ defmodule NumscriptexTest do
|> Numscriptex.Run.put!(:metadata, metadata)
|> Numscriptex.Run.put!(:variables, variables)
end

describe "version/0" do
test "shows both the Numscript-WASM and NumscriptEx versions" do
numscriptex_version =
:numscriptex
|> Application.spec(:vsn)
|> to_string()

[numscript_wasm_version] = AssetsManager.__info__(:attributes)[:numscript_wasm_version]

versions = Numscriptex.version()

assert versions.numscript_wasm == "v#{numscript_wasm_version}"
assert versions.numscriptex == "v#{numscriptex_version}"
end

@tag :tmp_dir
test "in case of errors numscript-wasm version is returned as 'unknown'", %{tmp_dir: tmp_dir} do
wasm_binary_path = AssetsManager.binary_path()
dest_path = Path.join(tmp_dir, "numscript.wasm")

File.copy!(wasm_binary_path, dest_path)
File.copy!(wasm_binary_path, wasm_binary_path, 1024)

numscriptex_version =
:numscriptex
|> Application.spec(:vsn)
|> to_string()

versions = Numscriptex.version()

assert versions.numscript_wasm == "unknown"
assert versions.numscriptex == "v#{numscriptex_version}"

File.copy!(dest_path, wasm_binary_path)
end
end
end