Packages
metastatic
0.8.4
0.26.0
0.25.0
0.24.1
0.24.0
0.23.0
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.3
0.20.2
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Cross-language code meta-model library using unified MetaAST representation. Parse, transform, and translate code across Python, Elixir, Ruby, Erlang, Haskell, and more via a shared three-tuple AST format.
Current section
Files
Jump to
Current section
Files
lib/metastatic/analysis/api_surface.ex
defmodule Metastatic.Analysis.ApiSurface do
@moduledoc """
API surface analysis for containers (modules/classes).
Analyzes the public interface, measuring complexity, consistency, and design quality.
## Metrics
- **Surface Size**: Number of public methods/properties
- **Parameter Complexity**: Average parameters per public method
- **Return Complexity**: Variety of return types
- **Naming Consistency**: Consistent naming patterns
- **Interface Cohesion**: How focused the API is
## Examples
# Small, focused API
ast = {:container, :class, "Stack", %{}, [
{:function_def, :public, "push", ["item"], %{}, ...},
{:function_def, :public, "pop", [], %{}, ...}
]}
doc = Document.new(ast, :python)
{:ok, result} = ApiSurface.analyze(doc)
result.surface_size # => 2
result.assessment # => :excellent
"""
alias Metastatic.Document
@type result :: %{
container_name: String.t() | nil,
surface_size: non_neg_integer(),
public_methods: [String.t()],
avg_params: float(),
max_params: non_neg_integer(),
assessment: :excellent | :good | :fair | :poor,
warnings: [String.t()],
recommendations: [String.t()]
}
use Metastatic.Document.Analyzer
@impl Metastatic.Document.Analyzer
def handle_analyze(%Document{ast: ast}, _opts \\ []) do
with {:ok, _type, name, members} <- extract_container(ast),
do: {:ok, analyze_api(name, members)}
end
defp extract_container({:container, type, name, _metadata, members}) do
{:ok, type, name, members}
end
defp extract_container(_), do: {:error, "AST does not contain a container"}
defp analyze_api(name, members) do
public_methods =
members
|> Enum.filter(&match?({:function_def, :public, _, _, _, _}, &1))
public_names = Enum.map(public_methods, fn {:function_def, _, name, _, _, _} -> name end)
param_counts =
Enum.map(public_methods, fn {:function_def, _, _, params, _, _} -> length(params) end)
surface_size = length(public_methods)
avg_params = if surface_size > 0, do: Enum.sum(param_counts) / surface_size, else: 0.0
max_params = if surface_size > 0, do: Enum.max(param_counts), else: 0
{assessment, warnings, recommendations} = assess_api(surface_size, avg_params, max_params)
%{
container_name: name,
surface_size: surface_size,
public_methods: Enum.sort(public_names),
avg_params: Float.round(avg_params, 2),
max_params: max_params,
assessment: assessment,
warnings: warnings,
recommendations: recommendations
}
end
defp assess_api(size, avg_params, max_params) do
warnings = []
recommendations = []
{warnings, recommendations} =
if size > 20 do
{["Large API surface (#{size} methods) - may be difficult to learn" | warnings],
["Consider splitting into multiple focused interfaces" | recommendations]}
else
{warnings, recommendations}
end
{warnings, recommendations} =
if avg_params > 4 do
{[
"High average parameter count (#{Float.round(avg_params, 1)}) - methods may be too complex"
| warnings
], ["Reduce parameter counts by using objects or builder patterns" | recommendations]}
else
{warnings, recommendations}
end
{warnings, recommendations} =
if max_params > 6 do
{["Method with #{max_params} parameters detected - too complex" | warnings],
recommendations}
else
{warnings, recommendations}
end
assessment =
cond do
size <= 10 and avg_params <= 3 and max_params <= 5 -> :excellent
size <= 15 and avg_params <= 4 and max_params <= 6 -> :good
size <= 25 and avg_params <= 5 -> :fair
true -> :poor
end
{assessment, Enum.reverse(warnings), Enum.reverse(recommendations)}
end
end