Packages
metastatic
0.3.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/mix/tasks/metastatic.detect_duplicates.ex
defmodule Mix.Tasks.Metastatic.DetectDuplicates do
@moduledoc """
Detects code duplication across MetaAST documents.
This task analyzes one or more files for code duplication, supporting
cross-language detection when files are parsed to MetaAST.
## Usage
mix metastatic.detect_duplicates FILE1 FILE2 [OPTIONS]
mix metastatic.detect_duplicates --dir PATH [OPTIONS]
## Options
* `--format FORMAT` - Output format: text (default), json, or detailed
* `--threshold FLOAT` - Similarity threshold for Type III detection (default: 0.8)
* `--output PATH` - Write output to file instead of stdout
* `--cross-language` - Enable cross-language detection (default: true)
* `--dir PATH` - Scan all files in directory recursively
* `--help` - Display this help message
## Examples
# Detect duplicates between two files
mix metastatic.detect_duplicates lib/foo.ex lib/bar.ex
# Scan entire directory
mix metastatic.detect_duplicates --dir lib/
# Output as JSON with custom threshold
mix metastatic.detect_duplicates lib/foo.ex lib/bar.ex --format json --threshold 0.85
# Save detailed report to file
mix metastatic.detect_duplicates --dir lib/ --format detailed --output report.txt
## Note
This task currently works with MetaAST documents. Language adapter support
(to parse real source files) will be added in future phases.
For now, you can use this task programmatically via the API:
alias Metastatic.{Document, Analysis.Duplication}
# Create documents
doc1 = Document.new(ast1, :elixir)
doc2 = Document.new(ast2, :python)
# Detect duplicates
{:ok, result} = Duplication.detect(doc1, doc2)
# Or detect across multiple documents
{:ok, groups} = Duplication.detect_in_list([doc1, doc2, doc3])
"""
@shortdoc "Detects code duplication across MetaAST documents"
use Mix.Task
@impl Mix.Task
def run(args) do
{opts, files, _} =
OptionParser.parse(args,
strict: [
format: :string,
threshold: :float,
output: :string,
cross_language: :boolean,
dir: :string,
help: :boolean
],
aliases: [
f: :format,
t: :threshold,
o: :output,
d: :dir,
h: :help
]
)
cond do
opts[:help] ->
show_help()
opts[:dir] ->
scan_directory(opts[:dir], opts)
length(files) >= 2 ->
detect_in_files(files, opts)
length(files) == 1 ->
Mix.shell().error("Error: Need at least 2 files to compare")
Mix.shell().info("Use --dir to scan a directory, or provide multiple files")
exit({:shutdown, 1})
true ->
show_help()
end
end
defp show_help do
Mix.shell().info(@moduledoc)
end
defp scan_directory(dir_path, _opts) do
Mix.shell().info("Scanning directory: #{dir_path}")
Mix.shell().info(
"Note: Directory scanning with real files requires language adapters (Phase 2+)"
)
Mix.shell().info("This feature will be available once language adapters are implemented.")
Mix.shell().info("")
Mix.shell().info("For now, use the API directly with MetaAST documents:")
Mix.shell().info(" Duplication.detect_in_list([doc1, doc2, ...])")
end
defp detect_in_files(files, opts) do
Mix.shell().info("Detecting duplicates in files:")
Enum.each(files, &Mix.shell().info(" - #{&1}"))
Mix.shell().info("")
Mix.shell().info("Note: File parsing requires language adapters (Phase 2+)")
Mix.shell().info("This feature will be available once language adapters are implemented.")
Mix.shell().info("")
Mix.shell().info("For now, use the API directly with MetaAST documents:")
Mix.shell().info(" doc1 = Document.new(ast1, :elixir)")
Mix.shell().info(" doc2 = Document.new(ast2, :python)")
Mix.shell().info(" {:ok, result} = Duplication.detect(doc1, doc2)")
Mix.shell().info(" Reporter.format(result, :#{opts[:format] || "text"})")
end
# Helper to format and output results (will be used when adapters are available)
# defp output_result(result, opts) do
# format = String.to_atom(opts[:format] || "text")
# output = Reporter.format(result, format)
#
# case opts[:output] do
# nil ->
# Mix.shell().info(output)
#
# path ->
# File.write!(path, output)
# Mix.shell().info("Report written to: #{path}")
# end
# end
#
# # Helper to format and output clone groups (will be used when adapters are available)
# defp output_groups(groups, opts) do
# format = String.to_atom(opts[:format] || "text")
# output = Reporter.format_groups(groups, format)
#
# case opts[:output] do
# nil ->
# Mix.shell().info(output)
#
# path ->
# File.write!(path, output)
# Mix.shell().info("Report written to: #{path}")
# end
# end
end