Packages
metastatic
0.24.0
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/cli.ex
defmodule Metastatic.CLI do
@moduledoc """
Shared utilities for Metastatic CLI commands.
Provides common functionality for Mix tasks including:
- Adapter lookup by language or file extension
- File path handling
- Error formatting
- Success/error reporting
"""
alias Metastatic.Languages
@type language :: atom()
@type file_path :: String.t()
@doc """
Get adapter for a given language.
Delegates to `Metastatic.Languages.get_adapter/1`.
## Examples
iex> Metastatic.CLI.get_adapter(:python)
{:ok, Metastatic.Adapters.Python}
iex> Metastatic.CLI.get_adapter(:unknown)
{:error, "Unsupported language: unknown. Supported: elixir, erlang, haskell, python, ruby"}
"""
@spec get_adapter(language()) :: {:ok, module()} | {:error, String.t()}
defdelegate get_adapter(language), to: Languages
@doc """
Detect language from file extension.
Delegates to `Metastatic.Languages.detect_language/1`.
## Examples
iex> Metastatic.CLI.detect_language("foo.py")
{:ok, :python}
iex> Metastatic.CLI.detect_language("foo.ex")
{:ok, :elixir}
iex> Metastatic.CLI.detect_language("foo.erl")
{:ok, :erlang}
iex> Metastatic.CLI.detect_language("foo.rb")
{:ok, :ruby}
iex> Metastatic.CLI.detect_language("foo.txt")
{:error, "Cannot detect language from extension: .txt"}
"""
@spec detect_language(file_path()) :: {:ok, language()} | {:error, String.t()}
defdelegate detect_language(path), to: Languages
@doc """
Parse a language string from CLI options into an atom.
Delegates to `Metastatic.Languages.parse_language/1`.
## Examples
iex> Metastatic.CLI.parse_language("python")
{:ok, :python}
iex> Metastatic.CLI.parse_language(nil)
nil
iex> Metastatic.CLI.parse_language("brainfuck")
{:error, "Invalid language: brainfuck. Supported: elixir, erlang, haskell, python, ruby"}
"""
@spec parse_language(String.t() | nil) :: {:ok, atom()} | {:error, String.t()} | nil
defdelegate parse_language(lang_str), to: Languages
@doc """
Read file contents.
## Examples
iex> Metastatic.CLI.read_file("test.py")
{:ok, "print('hello')"}
iex> Metastatic.CLI.read_file("nonexistent.py")
{:error, "Cannot read file test.py: enoent"}
"""
@spec read_file(file_path()) :: {:ok, String.t()} | {:error, String.t()}
def read_file(path) do
case File.read(path) do
{:ok, content} -> {:ok, content}
{:error, reason} -> {:error, "Cannot read file #{path}: #{reason}"}
end
end
@doc """
Write file contents, creating directories if needed.
## Examples
iex> Metastatic.CLI.write_file("output/test.py", "print('hello')")
:ok
"""
@spec write_file(file_path(), String.t()) :: :ok | {:error, String.t()}
def write_file(path, content) do
case File.mkdir_p(Path.dirname(path)) do
:ok ->
case File.write(path, content) do
:ok -> :ok
{:error, reason} -> {:error, "Cannot write file #{path}: #{reason}"}
end
{:error, reason} ->
{:error, "Cannot create directory for #{path}: #{reason}"}
end
end
@doc """
Format error message for CLI output.
Prefixes with red "Error: " for terminal output.
"""
@spec format_error(String.t()) :: String.t()
def format_error(message) do
IO.ANSI.format([:red, :bright, "Error: ", :reset, message]) |> IO.iodata_to_binary()
end
@doc """
Format success message for CLI output.
Prefixes with green checkmark for terminal output.
"""
@spec format_success(String.t()) :: String.t()
def format_success(message) do
IO.ANSI.format([:green, :bright, "✓ ", :reset, message]) |> IO.iodata_to_binary()
end
@doc """
Format info message for CLI output.
Prefixes with blue info icon for terminal output.
"""
@spec format_info(String.t()) :: String.t()
def format_info(message) do
IO.ANSI.format([:blue, :bright, "→ ", :reset, message]) |> IO.iodata_to_binary()
end
@doc """
Print error and exit with status code 1.
"""
@spec fatal(String.t()) :: no_return()
def fatal(message) do
Mix.shell().error(format_error(message))
exit({:shutdown, 1})
end
end