in doc-architect/doc-architect-core/src/main/java/com/docarchitect/core/scanner/ScanStatistics.java [43:217]
public record ScanStatistics(
int filesDiscovered,
int filesScanned,
int filesParsedSuccessfully,
int filesParsedWithFallback,
int filesFailed,
Map<String, Integer> errorCounts,
List<String> topErrors
) {
/**
* Compact constructor with validation and defaults.
*/
public ScanStatistics {
if (filesDiscovered < 0) {
filesDiscovered = 0;
}
if (filesScanned < 0) {
filesScanned = 0;
}
if (filesParsedSuccessfully < 0) {
filesParsedSuccessfully = 0;
}
if (filesParsedWithFallback < 0) {
filesParsedWithFallback = 0;
}
if (filesFailed < 0) {
filesFailed = 0;
}
if (errorCounts == null) {
errorCounts = Map.of();
}
if (topErrors == null) {
topErrors = List.of();
}
}
/**
* Creates an empty statistics instance (no files processed).
*
* @return empty statistics
*/
public static ScanStatistics empty() {
return new ScanStatistics(0, 0, 0, 0, 0, Map.of(), List.of());
}
/**
* Calculates the success rate of primary parsing (AST).
*
* @return success rate as percentage (0.0 to 100.0), or 0 if no files scanned
*/
public double getSuccessRate() {
if (filesScanned == 0) {
return 0.0;
}
return (filesParsedSuccessfully * 100.0) / filesScanned;
}
/**
* Calculates the overall parse rate (primary + fallback).
*
* @return overall parse rate as percentage (0.0 to 100.0), or 0 if no files scanned
*/
public double getOverallParseRate() {
if (filesScanned == 0) {
return 0.0;
}
return ((filesParsedSuccessfully + filesParsedWithFallback) * 100.0) / filesScanned;
}
/**
* Calculates the failure rate.
*
* @return failure rate as percentage (0.0 to 100.0), or 0 if no files scanned
*/
public double getFailureRate() {
if (filesScanned == 0) {
return 0.0;
}
return (filesFailed * 100.0) / filesScanned;
}
/**
* Returns true if this scan had any failures.
*
* @return true if at least one file failed to parse
*/
public boolean hasFailures() {
return filesFailed > 0;
}
/**
* Returns true if fallback parsing was used.
*
* @return true if at least one file was parsed via fallback
*/
public boolean usedFallback() {
return filesParsedWithFallback > 0;
}
/**
* Returns a human-readable summary of the statistics.
*
* @return summary string
*/
public String getSummary() {
return String.format(
"Discovered: %d, Scanned: %d, Success: %d (%.1f%%), Fallback: %d, Failed: %d (%.1f%%)",
filesDiscovered,
filesScanned,
filesParsedSuccessfully,
getSuccessRate(),
filesParsedWithFallback,
filesFailed,
getFailureRate()
);
}
/**
* Builder for constructing ScanStatistics incrementally.
*/
public static class Builder {
private int filesDiscovered = 0;
private int filesScanned = 0;
private int filesParsedSuccessfully = 0;
private int filesParsedWithFallback = 0;
private int filesFailed = 0;
private final Map<String, Integer> errorCounts = new java.util.HashMap<>();
private final List<String> topErrors = new java.util.ArrayList<>();
public Builder filesDiscovered(int count) {
this.filesDiscovered = count;
return this;
}
public Builder incrementFilesScanned() {
this.filesScanned++;
return this;
}
public Builder incrementFilesParsedSuccessfully() {
this.filesParsedSuccessfully++;
return this;
}
public Builder incrementFilesParsedWithFallback() {
this.filesParsedWithFallback++;
return this;
}
public Builder incrementFilesFailed() {
this.filesFailed++;
return this;
}
public Builder addError(String errorType, String errorDetail) {
errorCounts.merge(errorType, 1, Integer::sum);
if (topErrors.size() < 10) {
topErrors.add(errorDetail);
}
return this;
}
public ScanStatistics build() {
return new ScanStatistics(
filesDiscovered,
filesScanned,
filesParsedSuccessfully,
filesParsedWithFallback,
filesFailed,
Map.copyOf(errorCounts),
List.copyOf(topErrors)
);
}
}
}