Packages
dagger
0.13.1
0.21.7
0.21.6
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.8
0.20.7
0.20.6
0.20.5
0.20.4
0.20.3
0.20.2
0.20.1
0.20.0
0.19.11
0.19.10
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.19
0.18.18
0.18.17
0.18.16
0.18.15
0.18.14
0.18.13
0.18.12
0.18.11
0.18.10
0.18.9
0.18.8
0.18.7
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.2
0.17.1
0.17.0
0.16.3
0.16.2
0.16.1
0.16.0
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.0
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.2
0.0.0
retired
Dagger SDK for Elixir
Current section
Files
Jump to
Current section
Files
lib/dagger/mod/object.ex
defmodule Dagger.Mod.Object do
@moduledoc """
Declare a module as an object type.
## Declare an object
Add `use Dagger.Mod.Object` to the Elixir module that want to be a
Dagger module and give a name through `:name` configuration:
defmodule Potato do
use Dagger.Mod.Object, name: "Potato"
# ...
end
The module also support documentation by using Elixir standard documentation,
`@moduledoc`.
## Declare a function
The module provides a `defn`, a macro for declare a function.
Let's declare a new function named `echo` that accepts a `name` as a string
and return a container that echo a name in the module `Potato` from the previous
section:
defmodule Potato do
use Dagger.Mod.Object, name: "Potato"
defn echo(name: String.t()) :: Dagger.Container.t() do
dag()
|> Dagger.Client.container()
|> Dagger.Container.from("alpine")
|> Dagger.Container.with_exec(["echo", name])
end
end
From the example above, the `defn` allows you to annotate a type to function
arguments and return type by using Elixir Typespec. The type will convert to
a Dagger type when registering a module.
The supported primitive types for now are:
1. `integer()` for a boolean type.
2. `boolean()` for a boolean type.
3. `String.t()` or `binary()` for a string type.
4. `list(type)` or `[type]` for a list type.
5. `type | nil` for optional type.
6. Any type that generated under `Dagger` namespace (`Dagger.Container.t()`,
`Dagger.Directory.t()`, etc.).
The function also support documentation by using Elixir standard documentation,
`@doc`.
"""
@type function_name() :: atom()
@type function_def() :: {function_name(), keyword()}
alias Dagger.Mod.Object.Defn
@doc """
Get module documentation.
Returns module doc string or `nil` if the given module didn't have a documentation.
"""
@spec get_module_doc(module()) :: String.t() | nil
def get_module_doc(module) do
with {module_doc, _} <- fetch_docs(module),
%{"en" => doc} <- module_doc do
String.trim(doc)
else
:none -> nil
:hidden -> nil
{:error, :module_not_found} -> nil
end
end
@doc """
Get function documentation.
Return doc string or `nil` if that function didn't have a documentation.
"""
@spec get_function_doc(module(), function_name()) :: String.t() | nil
def get_function_doc(module, name) do
fun = fn
{{:function, ^name, _}, _, _, _, _} -> true
_ -> false
end
with {_, function_docs} <- fetch_docs(module),
{{:function, ^name, _}, _, _, doc_content, _} <- Enum.find(function_docs, fun),
%{"en" => doc} <- doc_content do
String.trim(doc)
else
nil -> nil
:none -> nil
:hidden -> nil
end
end
defp fetch_docs(module) do
{:docs_v1, _, :elixir, _, module_doc, _, function_docs} = Code.fetch_docs(module)
{module_doc, function_docs}
end
defmacro __using__(opts) do
name = opts[:name]
quote do
import Dagger.Mod.Object, only: [defn: 2]
import Dagger.Global, only: [dag: 0]
Module.register_attribute(__MODULE__, :function, accumulate: true, persist: true)
# Get an object name
def __object__(:name), do: unquote(name)
# List available function definitions.
def __object__(:functions) do
__MODULE__.__info__(:attributes)
|> Keyword.get_values(:function)
|> Enum.flat_map(& &1)
end
# Get a function definition.
def __object__(:function, name) do
__object__(:functions)
|> Keyword.fetch!(name)
end
end
end
@doc """
Declare a function.
"""
defmacro defn(call, do: block) do
{name, args, return} = extract_call(call)
has_self? = is_tuple(args)
arg_defs = compile_args(args)
return_def = compile_typespec!(return)
quote do
@function {unquote(name),
[self: unquote(has_self?), args: unquote(arg_defs), return: unquote(return_def)]}
unquote(Defn.define(name, args, return, block))
end
end
defp extract_call({:"::", _, [call_def, return]}) do
{name, args} = extract_call_def(call_def)
{name, args, return}
end
defp extract_call_def({name, _, []}) do
{name, []}
end
defp extract_call_def({name, _, [args]}) do
{name, args}
end
defp extract_call_def({name, _, [self, args]}) do
{name, {self, args}}
end
defp compile_args({_, args}) do
compile_args(args)
end
defp compile_args(args) do
for {name, spec} <- args do
{name, [type: compile_typespec!(spec)]}
end
end
defp compile_typespec!({:integer, _, []}), do: :integer
defp compile_typespec!({:boolean, _, []}), do: :boolean
## String
defp compile_typespec!({:binary, _, []}), do: :string
defp compile_typespec!(
{{:., _,
[
{:__aliases__, _, [:String]},
:t
]}, _, []}
) do
:string
end
defp compile_typespec!({{:., _, [{:__aliases__, _, module}, :t]}, _, []}) do
Module.concat(module)
end
## List
defp compile_typespec!({:list, _, [type]}) do
{:list, compile_typespec!(type)}
end
defp compile_typespec!([type]) do
{:list, compile_typespec!(type)}
end
## Optional
defp compile_typespec!({:|, _, [type, nil]}) do
{:optional, compile_typespec!(type)}
end
defp compile_typespec!(unsupported_type) do
raise ArgumentError, "type `#{Macro.to_string(unsupported_type)}` is not supported"
end
end