Packages

Run WebAssembly from Elixir. Load WASM modules in Rust, Go, C — call them like native functions.

Current section

Files

Jump to
firebird lib firebird phoenix form_wasm.ex
Raw

lib/firebird/phoenix/form_wasm.ex

defmodule Firebird.Phoenix.FormWasm do
@moduledoc """
WASM-accelerated HTML form building.
Provides high-performance form generation using a compiled WASM module.
Handles HTML escaping, CSRF token injection, method spoofing, and
Phoenix-compatible form field naming conventions.
This is the WASM-backed counterpart to `Firebird.Phoenix.Form` which
is pure Elixir. The WASM version is faster for complex forms with many
fields due to zero-copy HTML escaping in WASM.
## Examples
{:ok, form} = Firebird.load("fixtures/phoenix_form.wasm")
# Build a form opening tag
{:ok, html} = FormWasm.build_form(form, "user", "/users",
method: :post, csrf_token: "abc123")
# => ~s(<form action="/users" method="post"><input type="hidden" name="_csrf_token" value="abc123"/>)
# Build an input field
{:ok, html} = FormWasm.text_input(form, "user", "name", value: "Alice")
# => ~s(<input type="text" name="user[name]" id="user_name" value="Alice"/>)
"""
alias Firebird.Phoenix.WasmHelper
@doc """
Build an HTML form opening tag with CSRF token and method spoofing.
## Options
- `:method` - HTTP method (default: :post). :put/:patch/:delete are spoofed
- `:csrf_token` - CSRF token string
- `:multipart` - If true, adds multipart/form-data enctype
- `:class` - CSS class
- `:id` - HTML id attribute
"""
@spec build_form(pid(), String.t(), String.t(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def build_form(instance, scope, action, opts \\ []) do
method = Keyword.get(opts, :method, :post) |> to_string()
csrf = Keyword.get(opts, :csrf_token, "")
multipart = if Keyword.get(opts, :multipart, false), do: "true", else: "false"
class = Keyword.get(opts, :class, "")
id = Keyword.get(opts, :id, "")
input = Enum.join([scope, action, method, csrf, multipart, class, id], "|")
WasmHelper.call_string(instance, "build_form", input)
end
@doc """
Build a text input field.
## Options
- `:value` - Current value
- `:class` - CSS class
- `:id` - Custom HTML id (default: scope_field)
- `:placeholder` - Placeholder text
- `:required` - Whether field is required
"""
@spec text_input(pid(), String.t(), String.t(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def text_input(instance, scope, field, opts \\ []) do
build_input(instance, "text", scope, field, opts)
end
@doc "Build an email input field."
@spec email_input(pid(), String.t(), String.t(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def email_input(instance, scope, field, opts \\ []) do
build_input(instance, "email", scope, field, opts)
end
@doc "Build a password input field."
@spec password_input(pid(), String.t(), String.t(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def password_input(instance, scope, field, opts \\ []) do
build_input(instance, "password", scope, field, opts)
end
@doc "Build a number input field."
@spec number_input(pid(), String.t(), String.t(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def number_input(instance, scope, field, opts \\ []) do
build_input(instance, "number", scope, field, opts)
end
@doc "Build a hidden input field."
@spec hidden_input(pid(), String.t(), String.t(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def hidden_input(instance, scope, field, opts \\ []) do
build_input(instance, "hidden", scope, field, opts)
end
defp build_input(instance, type, scope, field, opts) do
value = Keyword.get(opts, :value, "") |> to_string()
class = Keyword.get(opts, :class, "")
id = Keyword.get(opts, :id, "")
placeholder = Keyword.get(opts, :placeholder, "")
required = if Keyword.get(opts, :required, false), do: "true", else: "false"
input = Enum.join([type, scope, field, value, class, id, placeholder, required], "|")
WasmHelper.call_string(instance, "build_input", input)
end
@doc """
Build a textarea element.
## Options
- `:value` - Current content
- `:rows` - Number of rows
- `:cols` - Number of columns
- `:class` - CSS class
- `:placeholder` - Placeholder text
- `:required` - Whether field is required
"""
@spec textarea(pid(), String.t(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
def textarea(instance, scope, field, opts \\ []) do
value = Keyword.get(opts, :value, "") |> to_string()
rows = Keyword.get(opts, :rows, "") |> to_string()
cols = Keyword.get(opts, :cols, "") |> to_string()
class = Keyword.get(opts, :class, "")
placeholder = Keyword.get(opts, :placeholder, "")
required = if Keyword.get(opts, :required, false), do: "true", else: "false"
input = Enum.join([scope, field, value, rows, cols, class, placeholder, required], "|")
WasmHelper.call_string(instance, "build_textarea", input)
end
@doc """
Build a select dropdown element.
## Parameters
- `options` - List of `{value, label}` tuples or list of strings
## Options
- `:selected` - Currently selected value
"""
@spec select(pid(), String.t(), String.t(), list(), keyword()) ::
{:ok, String.t()} | {:error, term()}
def select(instance, scope, field, options, opts \\ []) do
selected = Keyword.get(opts, :selected, "") |> to_string()
header = Enum.join([scope, field, selected], "|")
option_lines =
Enum.map(options, fn
{value, label} -> "#{value}|#{label}"
value -> "#{value}|#{value}"
end)
input = Enum.join([header | option_lines], "\n")
WasmHelper.call_string(instance, "build_select", input)
end
@doc """
Build a checkbox input with hidden field for unchecked state.
## Options
- `:value` - Value when checked (default: "true")
- `:checked` - Whether checked (default: false)
- `:label` - Label text
"""
@spec checkbox(pid(), String.t(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
def checkbox(instance, scope, field, opts \\ []) do
value = Keyword.get(opts, :value, "true") |> to_string()
checked = if Keyword.get(opts, :checked, false), do: "true", else: "false"
label = Keyword.get(opts, :label, "")
input = Enum.join([scope, field, value, checked, label], "|")
WasmHelper.call_string(instance, "build_checkbox", input)
end
@doc """
HTML-escape a string using WASM.
"""
@spec html_escape(pid(), String.t()) :: {:ok, String.t()} | {:error, term()}
def html_escape(instance, text) do
WasmHelper.call_string(instance, "html_escape", text)
end
end