ManualPrepareCommand.java
package org.egothor.methodatlas.command;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.egothor.methodatlas.CliConfig;
import org.egothor.methodatlas.ManualMode;
import org.egothor.methodatlas.ai.AiSuggestionException;
import org.egothor.methodatlas.ai.ManualPrepareEngine;
import org.egothor.methodatlas.ai.PromptBuilder;
import org.egothor.methodatlas.api.DiscoveredMethod;
import org.egothor.methodatlas.api.TestDiscovery;
import org.egothor.methodatlas.api.TestDiscoveryConfig;
/**
* CLI command handler for the {@code -manual-prepare} mode.
*
* <p>
* Discovers test classes via the configured {@link TestDiscovery} providers,
* then writes one work file per class to the prepare work directory.
* No CSV output is produced; only progress lines are written to the
* supplied {@link PrintWriter}.
* </p>
*
* @see org.egothor.methodatlas.ai.ManualPrepareEngine
* @see org.egothor.methodatlas.ai.ManualConsumeEngine
*/
public final class ManualPrepareCommand implements Command {
private static final Logger LOG = Logger.getLogger(ManualPrepareCommand.class.getName());
private final ManualMode.Prepare prepare;
private final CliConfig cliConfig;
private final TestDiscoveryConfig discoveryConfig;
/**
* Creates a new manual-prepare command.
*
* @param prepare manual prepare mode configuration (work and response dirs)
* @param cliConfig full parsed CLI configuration
* @param discoveryConfig discovery configuration forwarded to providers
*/
public ManualPrepareCommand(ManualMode.Prepare prepare, CliConfig cliConfig,
TestDiscoveryConfig discoveryConfig) {
this.prepare = prepare;
this.cliConfig = cliConfig;
this.discoveryConfig = discoveryConfig;
}
/**
* Discovers test classes, writes work files, and reports progress.
*
* @param out writer for progress and summary output
* @return {@code 0} if all files were processed successfully, {@code 1} if any
* provider encountered a processing error
* @throws IOException if traversing a file tree fails
*/
@Override
@SuppressWarnings("PMD.CloseResource") // providers closed by closeAll() in the finally block
public int execute(PrintWriter out) throws IOException {
ManualPrepareEngine engine;
try {
engine = new ManualPrepareEngine(prepare.workDir(), prepare.responseDir(), cliConfig.aiOptions());
} catch (AiSuggestionException e) {
throw new IllegalStateException("Failed to initialize manual prepare engine", e);
}
List<Path> roots = cliConfig.paths().isEmpty() ? List.of(Paths.get(".")) : cliConfig.paths();
List<TestDiscovery> providers = CommandSupport.loadProviders(discoveryConfig);
boolean hadErrors = false;
int prepared = 0;
try {
for (Path root : roots) {
List<DiscoveredMethod> allMethods = new ArrayList<>(); // NOPMD – intentionally one list per scan root
for (TestDiscovery provider : providers) {
provider.discover(root).forEach(allMethods::add);
if (provider.hadErrors()) {
hadErrors = true;
}
}
// Group by FQCN so each class produces one work file.
Map<String, List<DiscoveredMethod>> byClass = allMethods.stream()
.collect(Collectors.groupingBy(DiscoveredMethod::fqcn,
LinkedHashMap::new, Collectors.toList()));
for (Map.Entry<String, List<DiscoveredMethod>> entry : byClass.entrySet()) {
String fqcn = entry.getKey();
List<DiscoveredMethod> classMethods = entry.getValue();
String classSource = classMethods.get(0).sourceContent().get().orElse(null);
if (classSource == null) {
continue;
}
String fileStem = classMethods.get(0).fileStem();
List<PromptBuilder.TargetMethod> targetMethods = classMethods.stream()
.map(CommandSupport::toTargetMethod).toList();
try {
Path workFile = engine.prepare(fileStem, fqcn, classSource, targetMethods);
out.println("Prepared: " + workFile);
prepared++;
} catch (AiSuggestionException e) {
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Failed to prepare work file for " + fqcn, e);
}
}
}
}
} finally {
CommandSupport.closeAll(providers);
}
out.println("Manual prepare complete. Wrote " + prepared + " work file(s) to " + prepare.workDir()
+ " (response stubs in " + prepare.responseDir() + ")");
return hadErrors ? 1 : 0;
}
}