feat: more clear definition and first base implementation for lua
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
108
lib/bds/scripting.ex
Normal file
108
lib/bds/scripting.ex
Normal file
@@ -0,0 +1,108 @@
|
||||
defmodule BDS.Scripting do
|
||||
@moduledoc """
|
||||
Facade for the configured user-script runtime.
|
||||
"""
|
||||
|
||||
alias BDS.Scripting.Runtime
|
||||
|
||||
@type job_status :: :queued | :running | :completed | :failed | :cancelled
|
||||
@type job_snapshot :: %{
|
||||
id: String.t(),
|
||||
status: job_status(),
|
||||
progress: map(),
|
||||
result: term() | nil,
|
||||
error: term() | nil,
|
||||
inserted_at: DateTime.t(),
|
||||
started_at: DateTime.t() | nil,
|
||||
finished_at: DateTime.t() | nil
|
||||
}
|
||||
|
||||
@spec runtime() :: module()
|
||||
def runtime do
|
||||
Application.fetch_env!(:bds, :scripting)
|
||||
|> Keyword.fetch!(:runtime)
|
||||
end
|
||||
|
||||
@spec validate(String.t()) :: :ok | {:error, term()}
|
||||
def validate(source) when is_binary(source) do
|
||||
runtime().validate(source)
|
||||
end
|
||||
|
||||
@spec execute(String.t(), String.t(), [term()], [Runtime.execution_option()]) ::
|
||||
{:ok, term()} | {:error, term()}
|
||||
def execute(source, entrypoint, args \\ [], opts \\ [])
|
||||
when is_binary(source) and is_binary(entrypoint) and is_list(args) and is_list(opts) do
|
||||
runtime().execute(source, entrypoint, args, opts)
|
||||
end
|
||||
|
||||
@spec start_job(String.t(), String.t(), [term()], [Runtime.execution_option()]) ::
|
||||
{:ok, job_snapshot()} | {:error, term()}
|
||||
def start_job(source, entrypoint, args \\ [], opts \\ [])
|
||||
when is_binary(source) and is_binary(entrypoint) and is_list(args) and is_list(opts) do
|
||||
job_id = "script-job-" <> Integer.to_string(System.unique_integer([:positive, :monotonic]))
|
||||
|
||||
job = %{
|
||||
id: job_id,
|
||||
status: :queued,
|
||||
progress: %{},
|
||||
result: nil,
|
||||
error: nil,
|
||||
inserted_at: DateTime.utc_now(),
|
||||
started_at: nil,
|
||||
finished_at: nil
|
||||
}
|
||||
|
||||
:ok = BDS.Scripting.JobStore.put_job(job)
|
||||
|
||||
child_spec =
|
||||
{BDS.Scripting.JobRunner,
|
||||
job_id: job_id,
|
||||
runtime: runtime(),
|
||||
source: source,
|
||||
entrypoint: entrypoint,
|
||||
args: args,
|
||||
opts: batch_job_defaults(opts)}
|
||||
|
||||
case DynamicSupervisor.start_child(BDS.Scripting.JobSupervisor, child_spec) do
|
||||
{:ok, _pid} -> {:ok, BDS.Scripting.JobStore.fetch_job!(job_id)}
|
||||
{:error, reason} ->
|
||||
:ok =
|
||||
BDS.Scripting.JobStore.update_job(job_id, %{
|
||||
status: :failed,
|
||||
error: reason,
|
||||
finished_at: DateTime.utc_now()
|
||||
})
|
||||
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@spec get_job(String.t()) :: job_snapshot() | nil
|
||||
def get_job(job_id) when is_binary(job_id) do
|
||||
BDS.Scripting.JobStore.fetch_job(job_id)
|
||||
end
|
||||
|
||||
@spec cancel_job(String.t()) :: :ok | {:error, :not_found | :not_running}
|
||||
def cancel_job(job_id) when is_binary(job_id) do
|
||||
case BDS.Scripting.JobStore.runner_for(job_id) do
|
||||
nil ->
|
||||
case BDS.Scripting.JobStore.fetch_job(job_id) do
|
||||
nil -> {:error, :not_found}
|
||||
_job -> {:error, :not_running}
|
||||
end
|
||||
|
||||
pid -> BDS.Scripting.JobRunner.cancel(pid)
|
||||
end
|
||||
end
|
||||
|
||||
defp batch_job_defaults(opts) do
|
||||
config = Application.fetch_env!(:bds, :scripting)
|
||||
|
||||
defaults = [
|
||||
timeout: Keyword.get(config, :job_timeout, :infinity),
|
||||
max_reductions: Keyword.get(config, :job_max_reductions, :none)
|
||||
]
|
||||
|
||||
Keyword.merge(defaults, opts)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user