Current section
Files
Jump to
Current section
Files
lib/mix/tasks/vibe.generate.ex
defmodule Mix.Tasks.Vibe.Generate do
use Mix.Task
alias Vibe.Generator
@shortdoc "Generate code snippets and templates"
@moduledoc """
Generates code snippets and templates based on specifications.
## Usage
mix vibe.generate TYPE NAME [DESCRIPTION] [OPTIONS]
## Examples
mix vibe.generate module MyApp.MyModule "A module for handling user authentication"
mix vibe.generate genserver MyApp.UserStore "A GenServer for storing user data"
mix vibe.generate supervisor MyApp.MySupervisor "A supervisor for my application"
mix vibe.generate test MyApp.MyModule
## Types
module Generate a basic module
genserver Generate a GenServer template
supervisor Generate a supervisor template
test Generate a test module
## Options
--output FILE Output file path, defaults to lib/NAME_PARTS/LAST_PART.ex or test/NAME_PARTS/LAST_PART_test.exs
--force Overwrite existing file
--format FORMAT Output format (text, json, markdown, default: config value or markdown)
--help, -h Show this help message
"""
@impl Mix.Task
def run(args) do
{opts, remaining_args, _} =
OptionParser.parse(args,
strict: [output: :string, force: :boolean, format: :string, help: :boolean],
aliases: [h: :help, f: :force, o: :output]
)
format = opts_to_format(opts)
cond do
Keyword.has_key?(opts, :help) ->
print_help()
length(remaining_args) >= 2 ->
[type, name | rest] = remaining_args
description = List.first(rest) || nil
generate_and_save(type, name, description, opts, format)
true ->
IO.puts("Error: Invalid arguments. See `mix help vibe.generate`")
print_help()
end
end
defp opts_to_format(opts) do
cond do
opts[:format] -> String.to_atom(opts[:format])
true -> Vibe.output_format()
end
end
defp generate_and_save(type, name, description, opts, format) do
# Generate code based on type
{result, file_extension} =
case String.downcase(type) do
"module" ->
{Generator.generate_module(name, description), ".ex"}
"genserver" ->
{Generator.generate_genserver(name, description), ".ex"}
"supervisor" ->
{Generator.generate_supervisor(name, description), ".ex"}
"test" ->
{Generator.generate_test(name), "_test.exs"}
_ ->
{{:error, "Unknown type: #{type}"}, ".ex"}
end
case result do
{:ok, code} ->
# Determine output file path if not specified
output_path =
case opts[:output] do
nil ->
auto_generate_path(type, name, file_extension)
path ->
path
end
# Check if file exists and handle --force option
if File.exists?(output_path) and not opts[:force] do
IO.puts("Error: File already exists: #{output_path}")
IO.puts("Use --force to overwrite")
else
# Save the generated code to the file
case Generator.save_to_file(code, output_path) do
{:ok, saved_path} ->
output =
case format do
:json ->
Jason.encode!(%{
success: true,
message: "File generated successfully",
path: saved_path,
type: type
}, pretty: true)
:markdown ->
"""
# Code Generated Successfully ✨
Type: `#{type}`
Path: `#{saved_path}`
🎵 **Good vibes sent to your codebase!** 🎵
"""
_ ->
"""
Code generated successfully!
Type: #{type}
Path: #{saved_path}
Good vibes sent to your codebase!
"""
end
IO.puts(output)
{:error, message} ->
print_error(message, format)
end
end
{:error, message} ->
print_error(message, format)
end
end
defp auto_generate_path(type, name, file_extension) do
# Break name into parts (e.g., "MyApp.User" -> ["MyApp", "User"])
parts = String.split(name, ".")
filename = Macro.underscore(List.last(parts))
# The base directory depends on the type
base_dir =
case String.downcase(type) do
"test" -> "test"
_ -> "lib"
end
# Build path from namespace parts except the last one
namespace_path =
if length(parts) > 1 do
parts
|> Enum.drop(-1)
|> Enum.map(&Macro.underscore/1)
|> Path.join()
else
""
end
# Assemble the final path
Path.join([base_dir, namespace_path, "#{filename}#{file_extension}"])
end
defp print_error(message, format) do
output =
case format do
:json -> Jason.encode!(%{error: message}, pretty: true)
:markdown -> "**Error:** #{message}"
_ -> "Error: #{message}"
end
IO.puts(output)
end
defp print_help do
IO.puts("mix vibe.generate - Generate code snippets and templates")
IO.puts("")
IO.puts("Usage:")
IO.puts(" mix vibe.generate TYPE NAME [DESCRIPTION] [OPTIONS]")
IO.puts("")
IO.puts("Examples:")
IO.puts(" mix vibe.generate module MyApp.MyModule \"A module for handling user authentication\"")
IO.puts(" mix vibe.generate genserver MyApp.UserStore \"A GenServer for storing user data\"")
IO.puts(" mix vibe.generate supervisor MyApp.MySupervisor \"A supervisor for my application\"")
IO.puts(" mix vibe.generate test MyApp.MyModule")
IO.puts("")
IO.puts("Types:")
IO.puts(" module Generate a basic module")
IO.puts(" genserver Generate a GenServer template")
IO.puts(" supervisor Generate a supervisor template")
IO.puts(" test Generate a test module")
IO.puts("")
IO.puts("Options:")
IO.puts(" --output FILE, -o Output file path, defaults to lib/NAME_PARTS/LAST_PART.ex or test/NAME_PARTS/LAST_PART_test.exs")
IO.puts(" --force, -f Overwrite existing file")
IO.puts(" --format FORMAT Output format (text, json, markdown, default: config value or markdown)")
IO.puts(" --help, -h Show this help message")
IO.puts("")
IO.puts("Feedback:")
IO.puts(" ✨ Create code with funky fresh vibes! 🎵")
end
end