Packages
metastatic
0.20.2
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/smells/result.ex
defmodule Metastatic.Analysis.Smells.Result do
@moduledoc """
Result structure for code smell detection.
Contains information about detected code smells, their severity,
and refactoring suggestions.
## Fields
- `:has_smells?` - Boolean indicating if any code smells were found
- `:smells` - List of detected smell details
- `:summary` - Human-readable summary of findings
- `:total_smells` - Count of detected smells
- `:by_severity` - Map of counts by severity
- `:by_type` - Map of counts by smell type
## Smell Types
- `:long_function` - Function with too many statements
- `:deep_nesting` - Excessive nesting depth
- `:magic_number` - Unexplained numeric literals
- `:complex_conditional` - Complex boolean expressions
- `:long_parameter_list` - Too many parameters
- `:duplicate_code` - Duplicated logic
## Examples
iex> result = Metastatic.Analysis.Smells.Result.new([])
iex> result.has_smells?
false
iex> smells = [%{type: :long_function, severity: :high, description: "test"}]
iex> result = Metastatic.Analysis.Smells.Result.new(smells)
iex> result.has_smells?
true
iex> result.total_smells
1
"""
@type smell :: %{
type: smell_type(),
severity: severity(),
description: String.t(),
suggestion: String.t(),
context: term(),
location: location() | nil
}
@type location :: %{
optional(:function) => String.t(),
optional(:module) => String.t(),
optional(:line) => non_neg_integer(),
optional(:arity) => non_neg_integer()
}
@type smell_type ::
:long_function
| :deep_nesting
| :magic_number
| :complex_conditional
| :long_parameter_list
| :duplicate_code
@type severity :: :critical | :high | :medium | :low
@type t :: %__MODULE__{
has_smells?: boolean(),
smells: [smell()],
summary: String.t(),
total_smells: non_neg_integer(),
by_severity: %{severity() => non_neg_integer()},
by_type: %{smell_type() => non_neg_integer()}
}
defstruct has_smells?: false,
smells: [],
summary: "No code smells detected",
total_smells: 0,
by_severity: %{},
by_type: %{}
@doc """
Creates a new result from a list of code smells.
## Examples
iex> Metastatic.Analysis.Smells.Result.new([])
%Metastatic.Analysis.Smells.Result{has_smells?: false, summary: "No code smells detected"}
iex> smells = [%{type: :long_function, severity: :high, description: "test", suggestion: "refactor", context: nil}]
iex> result = Metastatic.Analysis.Smells.Result.new(smells)
iex> result.has_smells?
true
"""
@spec new([smell()]) :: t()
def new([]), do: %__MODULE__{}
def new([_ | _] = smells) do
by_severity = count_by_severity(smells)
by_type = count_by_type(smells)
%__MODULE__{
has_smells?: true,
smells: smells,
summary: build_summary(smells, by_severity, by_type),
total_smells: length(smells),
by_severity: by_severity,
by_type: by_type
}
end
@doc """
Creates a result with no code smells.
## Examples
iex> result = Metastatic.Analysis.Smells.Result.no_smells()
iex> result.has_smells?
false
"""
@spec no_smells() :: t()
def no_smells do
new([])
end
@doc """
Converts result to JSON-compatible map.
## Examples
iex> result = Metastatic.Analysis.Smells.Result.new([])
iex> map = Metastatic.Analysis.Smells.Result.to_map(result)
iex> is_map(map)
true
"""
@spec to_map(t()) :: map()
def to_map(%__MODULE__{} = result) do
%{
has_smells: result.has_smells?,
summary: result.summary,
total_smells: result.total_smells,
by_severity: result.by_severity,
by_type: result.by_type,
smells: result.smells
}
end
# Private helpers
defp count_by_severity(smells) do
Enum.reduce(smells, %{}, fn %{severity: sev}, acc ->
Map.update(acc, sev, 1, &(&1 + 1))
end)
end
defp count_by_type(smells) do
Enum.reduce(smells, %{}, fn %{type: type}, acc ->
Map.update(acc, type, 1, &(&1 + 1))
end)
end
defp build_summary(smells, by_severity, _by_type) do
total = length(smells)
severity_summary =
by_severity
|> Enum.sort_by(fn {sev, _} -> severity_order(sev) end, :desc)
|> Enum.map_join(", ", fn {sev, count} ->
"#{count} #{sev}"
end)
"Found #{total} code smell(s): #{severity_summary}"
end
defp severity_order(:critical), do: 4
defp severity_order(:high), do: 3
defp severity_order(:medium), do: 2
defp severity_order(:low), do: 1
end