Descripex (descripex v0.7.0)

Copy Markdown View Source

Single-source API declarations for self-describing Elixir functions.

The api macro is the sole source of truth for function documentation. It generates @doc text, emits @doc hints: metadata for machine consumption, validates param names at compile time, and produces __api__/0 and __api__/1 introspection functions.

Usage

defmodule MyLib.Funding do
  use Descripex, namespace: "/funding"

  api(:annualize, "Annualize a per-period funding rate to APR.",
    params: [
      rate: [kind: :value, description: "Per-period funding rate as decimal"],
      period_hours: [kind: :value, default: 8, description: "Hours per period"]
    ],
    returns: %{type: :float, description: "Annualized percentage rate (APR)"}
  )

  @spec annualize(number(), pos_integer()) :: float()
  def annualize(rate, period_hours \\ 8), do: ...
end

No separate @doc block needed — the macro generates it from the declaration.

Introspection

MyLib.Funding.__api__()
# => [%{name: :annualize, arity: 2, ...}, ...]

MyLib.Funding.__api__(:annualize)
# => %{name: :annualize, arity: 2, param_order: [:rate, :period_hours], spec: "...", hints: %{...}}

The param_order field lists the positional parameter names in declaration order (including defaulted params). Consumers that dispatch named arguments positionally — e.g. mapping MCP/JSON tool arguments onto apply(module, fun, args)must order arguments by param_order, not by Map.keys(hints.params). The hints[:params] map discards declaration order, so Map.keys/1 returns hash order and silently swaps multi-parameter calls.

param_order lists every declared positional param, including those with defaults. A consumer that omits an optional argument must dispatch on the function's lower arity rather than blindly mapping all of param_order — the defaulted tail can be dropped from the right.

Summary

Functions

Return the list of modules registered with this library.

Build machine-readable hints map from an api declaration's description and options.

Return a Level 1 overview of all modules in this library.

Return Level 2 function list for a module (by full atom or short name).

Return Level 3 function detail (or nil if not found).

Enrich compile-time api entries with specs fetched at runtime.

Generate human-readable @doc text from an api declaration's description and options.

Functions

__descripex_modules__()

@spec __descripex_modules__() :: [module()]

Return the list of modules registered with this library.

build_hints(description, opts)

@spec build_hints(
  String.t(),
  keyword()
) :: map()

Build machine-readable hints map from an api declaration's description and options.

describe()

@spec describe() :: [map()]

Return a Level 1 overview of all modules in this library.

describe(mod_or_short)

@spec describe(module() | atom()) :: [map()]

Return Level 2 function list for a module (by full atom or short name).

describe(mod_or_short, func_name)

@spec describe(module() | atom(), atom()) :: map() | nil

Return Level 3 function detail (or nil if not found).

enrich_with_specs(module, entries)

@spec enrich_with_specs(module(), [map()]) :: [map()]

Enrich compile-time api entries with specs fetched at runtime.

generate_doc(description, opts)

@spec generate_doc(
  String.t(),
  keyword()
) :: String.t()

Generate human-readable @doc text from an api declaration's description and options.