Packages

A comprehensive state management library for Phoenix LiveView with DSL, type checking, and deep Phoenix integration.

Current section

Files

Jump to
live_schema lib mix tasks live_schema.validate.ex
Raw

lib/mix/tasks/live_schema.validate.ex

defmodule Mix.Tasks.LiveSchema.Validate do
@shortdoc "Validates all LiveSchema modules in the project"
@moduledoc """
Validates all LiveSchema modules in the project compile correctly.
$ mix live_schema.validate
This task will:
1. Find all modules that `use LiveSchema`
2. Verify they compile without errors
3. Check that all field types are valid
4. Report any issues found
## Options
- `--verbose` - Print detailed information about each module
## Examples
$ mix live_schema.validate
$ mix live_schema.validate --verbose
"""
use Mix.Task
@impl Mix.Task
def run(args) do
{opts, _, _} = OptionParser.parse(args, switches: [verbose: :boolean])
verbose = opts[:verbose] || false
Mix.Task.run("compile", [])
modules = find_live_schema_modules()
if Enum.empty?(modules) do
Mix.shell().info("No LiveSchema modules found.")
else
Mix.shell().info("Found #{length(modules)} LiveSchema module(s)\n")
results =
Enum.map(modules, fn module ->
validate_module(module, verbose)
end)
errors = Enum.filter(results, &match?({:error, _, _}, &1))
successes = Enum.filter(results, &match?({:ok, _}, &1))
Mix.shell().info("\n#{IO.ANSI.bright()}Summary:#{IO.ANSI.reset()}")
Mix.shell().info(" #{IO.ANSI.green()}Passed: #{length(successes)}#{IO.ANSI.reset()}")
if length(errors) > 0 do
Mix.shell().info(" #{IO.ANSI.red()}Failed: #{length(errors)}#{IO.ANSI.reset()}")
System.halt(1)
end
end
end
defp find_live_schema_modules do
# Get all compiled modules
{:ok, modules} = :application.get_key(Mix.Project.config()[:app], :modules)
modules
|> Enum.filter(fn module ->
Code.ensure_loaded?(module) and function_exported?(module, :__live_schema__, 1)
end)
rescue
_ -> []
end
defp validate_module(module, verbose) do
if verbose do
Mix.shell().info("Validating #{inspect(module)}...")
end
try do
# Check fields exist
fields = module.__live_schema__(:fields)
# Validate each field's type
Enum.each(fields, fn field ->
info = module.__live_schema__({:field, field})
validate_type(info.type)
end)
# Try to create a new instance
_instance = module.new()
if verbose do
Mix.shell().info(" #{IO.ANSI.green()}OK#{IO.ANSI.reset()} - #{length(fields)} fields")
else
Mix.shell().info(" #{IO.ANSI.green()}[OK]#{IO.ANSI.reset()} #{inspect(module)}")
end
{:ok, module}
rescue
e ->
Mix.shell().info(" #{IO.ANSI.red()}[FAIL]#{IO.ANSI.reset()} #{inspect(module)}")
Mix.shell().info(" Error: #{Exception.message(e)}")
{:error, module, e}
end
end
defp validate_type(:string), do: :ok
defp validate_type(:integer), do: :ok
defp validate_type(:float), do: :ok
defp validate_type(:boolean), do: :ok
defp validate_type(:atom), do: :ok
defp validate_type(:any), do: :ok
defp validate_type(:map), do: :ok
defp validate_type(:list), do: :ok
defp validate_type({:list, inner}), do: validate_type(inner)
defp validate_type({:map, k, v}), do: (validate_type(k); validate_type(v))
defp validate_type({:enum, values}) when is_list(values), do: :ok
defp validate_type({:struct, module}) when is_atom(module), do: :ok
defp validate_type({:tuple, types}) when is_list(types), do: Enum.each(types, &validate_type/1)
defp validate_type(other), do: raise("Invalid type: #{inspect(other)}")
end