Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib raxol cli error_formatter.ex
Raw

lib/raxol/cli/error_formatter.ex

defmodule Raxol.CLI.ErrorFormatter do
@moduledoc """
Formats ErrorExperience suggestions for CLI output.
Bridges the sophisticated error analysis from `Raxol.Core.ErrorExperience`
to user-friendly CLI output using the `Raxol.CLI.Colors` palette.
## Usage
# Format and display an error with suggestions
ErrorFormatter.format_error(error, context)
# Get formatted string without printing
output = ErrorFormatter.to_string(error, context)
# Use in Mix tasks
case compile_project() do
{:error, reason} ->
ErrorFormatter.format_error(reason, %{task: :compile})
Mix.raise("Compilation failed")
:ok -> :ok
end
"""
alias Raxol.CLI.Colors
alias Raxol.Core.ErrorExperience
@doc """
Formats and prints an error with intelligent suggestions.
Analyzes the error, generates suggestions, and displays them
in a user-friendly CLI format.
"""
@spec format_error(term(), map()) :: :ok
def format_error(error, context \\ %{}) do
enhanced = ErrorExperience.classify_and_enhance(error, context)
output = format_enhanced_error(enhanced)
IO.puts(output)
:ok
end
@doc """
Returns formatted error string without printing.
"""
@spec to_string(term(), map()) :: String.t()
def to_string(error, context \\ %{}) do
enhanced = ErrorExperience.classify_and_enhance(error, context)
format_enhanced_error(enhanced)
end
@doc """
Formats an already-enhanced error structure.
"""
@spec format_enhanced(ErrorExperience.enhanced_error()) :: :ok
def format_enhanced(enhanced_error) do
output = format_enhanced_error(enhanced_error)
IO.puts(output)
:ok
end
# ============================================================================
# Formatting Functions
# ============================================================================
defp format_enhanced_error(enhanced) do
sections = [
format_header(enhanced),
format_error_details(enhanced),
format_suggestions(enhanced.suggestions),
format_performance_context(enhanced),
format_next_steps(enhanced)
]
sections
|> Enum.reject(&is_nil/1)
|> Enum.join("\n")
end
defp format_header(enhanced) do
severity_indicator = severity_to_indicator(enhanced.severity)
category = enhanced.category |> Atom.to_string() |> String.replace("_", " ")
"""
#{Colors.divider("-", 60)}
#{severity_indicator} #{Colors.bold(String.upcase(category) <> " ERROR")}
#{Colors.divider("-", 60)}
"""
end
defp format_error_details(enhanced) do
impact_color = impact_to_color(enhanced.performance_impact)
base_lines = [
" #{Colors.muted("Category:")} #{enhanced.category}",
" #{Colors.muted("Severity:")} #{enhanced.severity}",
" #{Colors.muted("Impact:")} #{impact_color.(Atom.to_string(enhanced.performance_impact))}"
]
related_line =
case enhanced.related_optimizations do
[] -> []
opts -> [" #{Colors.muted("Related:")} #{Enum.join(opts, ", ")}"]
end
(base_lines ++ related_line)
|> Enum.join("\n")
end
defp format_suggestions([]), do: nil
defp format_suggestions(suggestions) do
header = "\n#{Colors.section_header("Suggestions")}\n"
formatted =
suggestions
|> Enum.take(3)
|> Enum.with_index(1)
|> Enum.map_join("\n", &format_single_suggestion/1)
header <> formatted
end
defp format_single_suggestion({suggestion, index}) do
confidence_pct = trunc(suggestion.confidence * 100)
confidence_bar = confidence_indicator(suggestion.confidence)
type_badge =
case suggestion.type do
:automatic -> Colors.success("[AUTO]")
:guided -> Colors.info("[GUIDED]")
:manual -> Colors.warning("[MANUAL]")
:documentation -> Colors.muted("[DOCS]")
end
base_lines = [
" #{Colors.bold("#{index}.")} #{suggestion.description}",
" #{type_badge} #{confidence_bar} #{confidence_pct}% confidence"
]
action_line =
if suggestion.action do
[" #{Colors.muted("Run:")} #{Colors.info(suggestion.action)}"]
else
[]
end
Enum.join(base_lines ++ action_line, "\n")
end
defp format_performance_context(%{performance_impact: :none}), do: nil
defp format_performance_context(enhanced) do
header = "\n#{Colors.subsection_header("Performance Context")}\n"
lines = [" Impact: #{enhanced.performance_impact}"]
lines =
case enhanced.context[:phase3_targets] do
nil ->
lines
targets ->
lines ++
Enum.map(targets, fn {k, v} ->
" Target #{k}: #{v}"
end)
end
header <> Enum.join(lines, "\n")
end
defp format_next_steps(enhanced) do
header = "\n#{Colors.subsection_header("Next Steps")}\n"
steps =
enhanced.suggestions
|> Enum.take(2)
|> Enum.filter(& &1.action)
|> Enum.map(fn s ->
" #{Colors.muted("->")} #{Colors.info(s.action)}"
end)
case steps do
[] ->
header <>
" #{Colors.muted("Run")} #{Colors.info("mix raxol.debug")} #{Colors.muted("for investigation")}"
_ ->
header <> Enum.join(steps, "\n")
end
end
# ============================================================================
# Helper Functions
# ============================================================================
defp severity_to_indicator(severity) do
case severity do
:critical -> Colors.error("[!!]")
:error -> Colors.status_indicator(:error)
:warning -> Colors.status_indicator(:warning)
:info -> Colors.status_indicator(:info)
:debug -> Colors.muted("[..]")
end
end
defp impact_to_color(impact) do
case impact do
:critical -> &Colors.error/1
:high -> &Colors.error/1
:medium -> &Colors.warning/1
:low -> &Colors.info/1
:none -> &Colors.muted/1
end
end
defp confidence_indicator(confidence) when confidence >= 0.8,
do: Colors.success("***")
defp confidence_indicator(confidence) when confidence >= 0.6,
do: Colors.info("**")
defp confidence_indicator(confidence) when confidence >= 0.4,
do: Colors.warning("*")
defp confidence_indicator(_), do: Colors.muted("*")
end