698 lines
20 KiB
C#
Executable File
698 lines
20 KiB
C#
Executable File
using MarketAlly.AIPlugin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MarketAlly.AIPlugin.Refactoring.Plugins
|
|
{
|
|
[AIPlugin("BatchRefactor", "Orchestrates multiple refactoring operations across files and projects")]
|
|
public class BatchRefactorPlugin : IAIPlugin
|
|
{
|
|
[AIParameter("Root directory to process", required: true)]
|
|
public string RootDirectory { get; set; }
|
|
|
|
[AIParameter("File pattern to match (e.g., *.cs)", required: false)]
|
|
public string FilePattern { get; set; } = "*.cs";
|
|
|
|
[AIParameter("Refactoring operations to perform (comma-separated)", required: true)]
|
|
public string Operations { get; set; }
|
|
|
|
[AIParameter("Configuration file path for refactoring rules", required: false)]
|
|
public string ConfigPath { get; set; }
|
|
|
|
[AIParameter("Maximum concurrent operations", required: false)]
|
|
public int MaxConcurrency { get; set; } = 3;
|
|
|
|
[AIParameter("Apply changes to files", required: false)]
|
|
public bool ApplyChanges { get; set; } = false;
|
|
|
|
[AIParameter("Create detailed operation log", required: false)]
|
|
public bool DetailedLogging { get; set; } = true;
|
|
|
|
[AIParameter("Stop on first error", required: false)]
|
|
public bool StopOnError { get; set; } = false;
|
|
|
|
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
|
|
{
|
|
["rootDirectory"] = typeof(string),
|
|
["rootdirectory"] = typeof(string), // Allow lowercase
|
|
["filePattern"] = typeof(string),
|
|
["filepattern"] = typeof(string), // Allow lowercase
|
|
["operations"] = typeof(string),
|
|
["configPath"] = typeof(string),
|
|
["configpath"] = typeof(string), // Allow lowercase
|
|
["maxConcurrency"] = typeof(int),
|
|
["maxconcurrency"] = typeof(int), // Allow lowercase
|
|
["applyChanges"] = typeof(bool),
|
|
["applychanges"] = typeof(bool), // Allow lowercase
|
|
["detailedLogging"] = typeof(bool),
|
|
["detailedlogging"] = typeof(bool), // Allow lowercase
|
|
["stopOnError"] = typeof(bool),
|
|
["stoponerror"] = typeof(bool) // Allow lowercase
|
|
};
|
|
|
|
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
|
|
{
|
|
try
|
|
{
|
|
// Extract parameters with case-insensitive handling
|
|
string rootDirectory = GetParameterValue(parameters, "rootDirectory", "rootdirectory")?.ToString();
|
|
string filePattern = GetParameterValue(parameters, "filePattern", "filepattern")?.ToString() ?? "*.cs";
|
|
string operations = GetParameterValue(parameters, "operations")?.ToString();
|
|
string configPath = GetParameterValue(parameters, "configPath", "configpath")?.ToString();
|
|
int maxConcurrency = GetIntParameter(parameters, "maxConcurrency", "maxconcurrency", 3);
|
|
bool applyChanges = GetBoolParameter(parameters, "applyChanges", "applychanges", false);
|
|
bool detailedLogging = GetBoolParameter(parameters, "detailedLogging", "detailedlogging", true);
|
|
bool stopOnError = GetBoolParameter(parameters, "stopOnError", "stoponerror", false);
|
|
|
|
// Validate inputs
|
|
if (!Directory.Exists(rootDirectory))
|
|
{
|
|
return new AIPluginResult(
|
|
new DirectoryNotFoundException($"Directory not found: {rootDirectory}"),
|
|
"Invalid root directory"
|
|
);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(operations))
|
|
{
|
|
return new AIPluginResult(
|
|
new ArgumentException("Operations parameter is required"),
|
|
"Missing operations parameter"
|
|
);
|
|
}
|
|
|
|
// Load configuration
|
|
if (configPath == null)
|
|
configPath = rootDirectory;
|
|
var config = await LoadConfiguration(configPath);
|
|
|
|
// Parse operations
|
|
var operationList = operations.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(op => op.Trim().ToLower())
|
|
.ToList();
|
|
|
|
// Discover files to process
|
|
var filesToProcess = DiscoverFiles(rootDirectory, filePattern, config);
|
|
|
|
if (!filesToProcess.Any())
|
|
{
|
|
return new AIPluginResult(new
|
|
{
|
|
Message = "No files found matching the criteria",
|
|
RootDirectory = rootDirectory,
|
|
FilePattern = filePattern,
|
|
FilesProcessed = 0
|
|
});
|
|
}
|
|
|
|
// Execute batch refactoring
|
|
var batchResult = await ExecuteBatchRefactoring(
|
|
filesToProcess,
|
|
operationList,
|
|
config,
|
|
maxConcurrency,
|
|
applyChanges,
|
|
detailedLogging,
|
|
stopOnError
|
|
);
|
|
|
|
// Generate summary
|
|
var summary = GenerateBatchSummary(batchResult, operationList, applyChanges);
|
|
|
|
return new AIPluginResult(new
|
|
{
|
|
Message = $"Batch refactoring completed: {batchResult.TotalFiles} files processed",
|
|
RootDirectory = rootDirectory,
|
|
FilePattern = filePattern,
|
|
Operations = operationList,
|
|
ChangesApplied = applyChanges,
|
|
Summary = summary,
|
|
DetailedResults = batchResult,
|
|
Timestamp = DateTime.UtcNow
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AIPluginResult(ex, $"Batch refactoring failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private async Task<RefactoringConfiguration> LoadConfiguration(string configPath)
|
|
{
|
|
var config = new RefactoringConfiguration();
|
|
|
|
if (!string.IsNullOrEmpty(configPath) && File.Exists(configPath))
|
|
{
|
|
try
|
|
{
|
|
var configJson = await File.ReadAllTextAsync(configPath);
|
|
config = JsonSerializer.Deserialize<RefactoringConfiguration>(configJson, new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Use default configuration if loading fails
|
|
config = new RefactoringConfiguration();
|
|
config.Errors.Add($"Failed to load configuration: {ex.Message}");
|
|
}
|
|
} else
|
|
{
|
|
config.RootDirectory = configPath;
|
|
}
|
|
|
|
// Set defaults if not specified
|
|
if (config.ExcludedFiles == null || !config.ExcludedFiles.Any())
|
|
config.ExcludedFiles = new List<string> { "*.Designer.cs", "*.generated.cs", "AssemblyInfo.cs" };
|
|
|
|
if (config.ExcludedDirectories == null || !config.ExcludedDirectories.Any())
|
|
config.ExcludedDirectories = new List<string> { "bin", "obj", ".git", ".vs", "packages", "node_modules" };
|
|
|
|
return config;
|
|
}
|
|
|
|
private List<string> DiscoverFiles(string rootDirectory, string filePattern, RefactoringConfiguration config)
|
|
{
|
|
var files = Directory.GetFiles(rootDirectory, filePattern, SearchOption.AllDirectories);
|
|
|
|
return files.Where(file => !ShouldExcludeFile(file, config)).ToList();
|
|
}
|
|
|
|
private bool ShouldExcludeFile(string filePath, RefactoringConfiguration config)
|
|
{
|
|
var fileName = Path.GetFileName(filePath);
|
|
var relativePath = Path.GetRelativePath(config.RootDirectory ?? "", filePath);
|
|
|
|
// Check excluded file patterns
|
|
foreach (var pattern in config.ExcludedFiles)
|
|
{
|
|
if (MatchesPattern(fileName, pattern))
|
|
return true;
|
|
}
|
|
|
|
// Check excluded directories
|
|
foreach (var excludedDir in config.ExcludedDirectories)
|
|
{
|
|
if (relativePath.Contains(excludedDir, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
}
|
|
|
|
// Check excluded patterns
|
|
foreach (var pattern in config.ExcludedPatterns)
|
|
{
|
|
if (MatchesPattern(relativePath, pattern))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool MatchesPattern(string input, string pattern)
|
|
{
|
|
// Simple pattern matching with * wildcard support
|
|
if (pattern.Contains('*'))
|
|
{
|
|
var regexPattern = pattern.Replace("*", ".*");
|
|
return System.Text.RegularExpressions.Regex.IsMatch(input, regexPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
|
}
|
|
|
|
return input.Equals(pattern, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private async Task<BatchRefactoringResult> ExecuteBatchRefactoring(
|
|
List<string> files,
|
|
List<string> operations,
|
|
RefactoringConfiguration config,
|
|
int maxConcurrency,
|
|
bool applyChanges,
|
|
bool detailedLogging,
|
|
bool stopOnError)
|
|
{
|
|
var result = new BatchRefactoringResult
|
|
{
|
|
TotalFiles = files.Count,
|
|
StartTime = DateTime.UtcNow,
|
|
FileResults = new List<BatchFileRefactoringResult>()
|
|
};
|
|
|
|
var semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency);
|
|
var cancellationTokenSource = new CancellationTokenSource();
|
|
|
|
var tasks = files.Select(async file =>
|
|
{
|
|
await semaphore.WaitAsync(cancellationTokenSource.Token);
|
|
|
|
try
|
|
{
|
|
var fileResult = await ProcessSingleFile(file, operations, config, applyChanges, detailedLogging, cancellationTokenSource.Token);
|
|
|
|
lock (result)
|
|
{
|
|
result.FileResults.Add(fileResult);
|
|
|
|
if (fileResult.Success)
|
|
{
|
|
result.SuccessfulFiles++;
|
|
}
|
|
else
|
|
{
|
|
result.FailedFiles++;
|
|
if (stopOnError)
|
|
{
|
|
cancellationTokenSource.Cancel();
|
|
}
|
|
}
|
|
}
|
|
|
|
return fileResult;
|
|
}
|
|
finally
|
|
{
|
|
semaphore.Release();
|
|
}
|
|
});
|
|
|
|
try
|
|
{
|
|
await Task.WhenAll(tasks);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
result.StoppedOnError = true;
|
|
}
|
|
|
|
result.EndTime = DateTime.UtcNow;
|
|
result.TotalDuration = result.EndTime - result.StartTime;
|
|
|
|
return result;
|
|
}
|
|
|
|
private async Task<BatchFileRefactoringResult> ProcessSingleFile(
|
|
string filePath,
|
|
List<string> operations,
|
|
RefactoringConfiguration config,
|
|
bool applyChanges,
|
|
bool detailedLogging,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var fileResult = new BatchFileRefactoringResult
|
|
{
|
|
FilePath = filePath,
|
|
FileName = Path.GetFileName(filePath),
|
|
StartTime = DateTime.UtcNow,
|
|
OperationResults = new List<OperationResult>()
|
|
};
|
|
|
|
try
|
|
{
|
|
foreach (var operation in operations)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var operationResult = await ExecuteOperation(filePath, operation, config, applyChanges, detailedLogging);
|
|
fileResult.OperationResults.Add(operationResult);
|
|
|
|
if (!operationResult.Success && config.StopOnFirstError)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
fileResult.Success = fileResult.OperationResults.All(or => or.Success);
|
|
fileResult.TotalOperations = fileResult.OperationResults.Count;
|
|
fileResult.SuccessfulOperations = fileResult.OperationResults.Count(or => or.Success);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fileResult.Success = false;
|
|
fileResult.Error = ex.Message;
|
|
}
|
|
finally
|
|
{
|
|
fileResult.EndTime = DateTime.UtcNow;
|
|
fileResult.Duration = fileResult.EndTime - fileResult.StartTime;
|
|
}
|
|
|
|
return fileResult;
|
|
}
|
|
|
|
private async Task<OperationResult> ExecuteOperation(
|
|
string filePath,
|
|
string operation,
|
|
RefactoringConfiguration config,
|
|
bool applyChanges,
|
|
bool detailedLogging)
|
|
{
|
|
var result = new OperationResult
|
|
{
|
|
OperationType = operation,
|
|
StartTime = DateTime.UtcNow
|
|
};
|
|
|
|
try
|
|
{
|
|
switch (operation.ToLower())
|
|
{
|
|
case "codeanalysis":
|
|
case "code-analysis":
|
|
result = await ExecuteCodeAnalysis(filePath, result, applyChanges);
|
|
break;
|
|
|
|
case "documentation":
|
|
case "add-documentation":
|
|
result = await ExecuteDocumentation(filePath, result, applyChanges);
|
|
break;
|
|
|
|
case "formatting":
|
|
case "format-code":
|
|
result = await ExecuteCodeFormatting(filePath, result, applyChanges);
|
|
break;
|
|
|
|
case "naming":
|
|
case "naming-conventions":
|
|
result = await ExecuteNamingConventions(filePath, result, applyChanges);
|
|
break;
|
|
|
|
case "cleanup":
|
|
case "code-cleanup":
|
|
result = await ExecuteCodeCleanup(filePath, result, applyChanges);
|
|
break;
|
|
|
|
default:
|
|
result.Success = false;
|
|
result.Error = $"Unknown operation: {operation}";
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.Error = ex.Message;
|
|
}
|
|
finally
|
|
{
|
|
result.EndTime = DateTime.UtcNow;
|
|
result.Duration = result.EndTime - result.StartTime;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private async Task<OperationResult> ExecuteCodeAnalysis(string filePath, OperationResult result, bool applyChanges)
|
|
{
|
|
try
|
|
{
|
|
var analysisPlugin = new CodeAnalysisPlugin();
|
|
var parameters = new Dictionary<string, object>
|
|
{
|
|
["path"] = filePath,
|
|
["analysisDepth"] = "detailed",
|
|
["includeComplexity"] = true,
|
|
["includeCodeSmells"] = true,
|
|
["includeSuggestions"] = true
|
|
};
|
|
|
|
var pluginResult = await analysisPlugin.ExecuteAsync(parameters);
|
|
|
|
result.Success = pluginResult.Success;
|
|
result.Details = pluginResult.Data;
|
|
result.Message = pluginResult.Message;
|
|
|
|
if (!pluginResult.Success)
|
|
{
|
|
result.Error = pluginResult.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.Error = ex.Message;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private async Task<OperationResult> ExecuteDocumentation(string filePath, OperationResult result, bool applyChanges)
|
|
{
|
|
try
|
|
{
|
|
var docPlugin = new EnhancedDocumentationGeneratorPlugin();
|
|
var parameters = new Dictionary<string, object>
|
|
{
|
|
["filePath"] = filePath,
|
|
["style"] = "intelligent",
|
|
["includeExamples"] = false,
|
|
["includeSeeAlso"] = false,
|
|
["applyChanges"] = applyChanges
|
|
};
|
|
|
|
var pluginResult = await docPlugin.ExecuteAsync(parameters);
|
|
|
|
result.Success = pluginResult.Success;
|
|
result.Details = pluginResult.Data;
|
|
result.Message = pluginResult.Message;
|
|
|
|
if (!pluginResult.Success)
|
|
{
|
|
result.Error = pluginResult.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.Error = ex.Message;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private async Task<OperationResult> ExecuteCodeFormatting(string filePath, OperationResult result, bool applyChanges)
|
|
{
|
|
try
|
|
{
|
|
var formatPlugin = new CodeFormatterPlugin();
|
|
var parameters = new Dictionary<string, object>
|
|
{
|
|
["path"] = filePath,
|
|
["formattingStyle"] = "microsoft",
|
|
["fixIndentation"] = true,
|
|
["organizeUsings"] = true,
|
|
["removeUnnecessary"] = true,
|
|
["applyChanges"] = applyChanges
|
|
};
|
|
|
|
var pluginResult = await formatPlugin.ExecuteAsync(parameters);
|
|
|
|
result.Success = pluginResult.Success;
|
|
result.Details = pluginResult.Data;
|
|
result.Message = pluginResult.Message;
|
|
|
|
if (!pluginResult.Success)
|
|
{
|
|
result.Error = pluginResult.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.Error = ex.Message;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private async Task<OperationResult> ExecuteNamingConventions(string filePath, OperationResult result, bool applyChanges)
|
|
{
|
|
try
|
|
{
|
|
var namingPlugin = new NamingConventionPlugin();
|
|
var parameters = new Dictionary<string, object>
|
|
{
|
|
["filePath"] = filePath,
|
|
["convention"] = "pascal",
|
|
["checkMeaningfulness"] = true,
|
|
["aiSuggestions"] = false, // Disable AI suggestions for batch processing
|
|
["applyChanges"] = applyChanges
|
|
};
|
|
|
|
var pluginResult = await namingPlugin.ExecuteAsync(parameters);
|
|
|
|
result.Success = pluginResult.Success;
|
|
result.Details = pluginResult.Data;
|
|
result.Message = pluginResult.Message;
|
|
|
|
if (!pluginResult.Success)
|
|
{
|
|
result.Error = pluginResult.Message;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.Error = ex.Message;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private async Task<OperationResult> ExecuteCodeCleanup(string filePath, OperationResult result, bool applyChanges)
|
|
{
|
|
try
|
|
{
|
|
// Execute multiple cleanup operations
|
|
var cleanupOperations = new[] { "formatting", "documentation" };
|
|
var cleanupResults = new List<object>();
|
|
|
|
foreach (var cleanup in cleanupOperations)
|
|
{
|
|
var cleanupResult = await ExecuteOperation(filePath, cleanup, new RefactoringConfiguration(), applyChanges, false);
|
|
cleanupResults.Add(new
|
|
{
|
|
Operation = cleanup,
|
|
Success = cleanupResult.Success,
|
|
Message = cleanupResult.Message
|
|
});
|
|
}
|
|
|
|
result.Success = cleanupResults.Cast<dynamic>().All(r => r.Success);
|
|
result.Details = new { CleanupOperations = cleanupResults };
|
|
result.Message = $"Executed {cleanupOperations.Length} cleanup operations";
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.Success = false;
|
|
result.Error = ex.Message;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private object GenerateBatchSummary(BatchRefactoringResult batchResult, List<string> operations, bool changesApplied)
|
|
{
|
|
var operationStats = batchResult.FileResults
|
|
.SelectMany(fr => fr.OperationResults)
|
|
.GroupBy(or => or.OperationType)
|
|
.ToDictionary(g => g.Key, g => new
|
|
{
|
|
Total = g.Count(),
|
|
Successful = g.Count(or => or.Success),
|
|
Failed = g.Count(or => !or.Success),
|
|
AverageDuration = g.Average(or => or.Duration.TotalMilliseconds)
|
|
});
|
|
|
|
var fileTypeStats = batchResult.FileResults
|
|
.GroupBy(fr => Path.GetExtension(fr.FilePath))
|
|
.ToDictionary(g => g.Key, g => new
|
|
{
|
|
Count = g.Count(),
|
|
Successful = g.Count(fr => fr.Success)
|
|
});
|
|
|
|
return new
|
|
{
|
|
TotalFiles = batchResult.TotalFiles,
|
|
SuccessfulFiles = batchResult.SuccessfulFiles,
|
|
FailedFiles = batchResult.FailedFiles,
|
|
TotalDuration = batchResult.TotalDuration,
|
|
AverageFileProcessingTime = batchResult.FileResults.Any()
|
|
? TimeSpan.FromMilliseconds(batchResult.FileResults.Average(fr => fr.Duration.TotalMilliseconds))
|
|
: TimeSpan.Zero,
|
|
ChangesApplied = changesApplied,
|
|
StoppedOnError = batchResult.StoppedOnError,
|
|
OperationStatistics = operationStats,
|
|
FileTypeStatistics = fileTypeStats,
|
|
TopErrors = batchResult.FileResults
|
|
.Where(fr => !fr.Success)
|
|
.GroupBy(fr => fr.Error)
|
|
.OrderByDescending(g => g.Count())
|
|
.Take(5)
|
|
.Select(g => new { Error = g.Key, Count = g.Count() })
|
|
.ToList()
|
|
};
|
|
}
|
|
|
|
// Helper methods for parameter extraction
|
|
private object GetParameterValue(IReadOnlyDictionary<string, object> parameters, params string[] keys)
|
|
{
|
|
foreach (var key in keys)
|
|
{
|
|
if (parameters.TryGetValue(key, out var value))
|
|
return value;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private bool GetBoolParameter(IReadOnlyDictionary<string, object> parameters, string key1, string key2, bool defaultValue = false)
|
|
{
|
|
var value = GetParameterValue(parameters, key1, key2);
|
|
return value != null ? Convert.ToBoolean(value) : defaultValue;
|
|
}
|
|
|
|
private int GetIntParameter(IReadOnlyDictionary<string, object> parameters, string key1, string key2, int defaultValue = 0)
|
|
{
|
|
var value = GetParameterValue(parameters, key1, key2);
|
|
return value != null ? Convert.ToInt32(value) : defaultValue;
|
|
}
|
|
}
|
|
|
|
// Supporting classes for batch refactoring
|
|
public class RefactoringConfiguration
|
|
{
|
|
public string RootDirectory { get; set; }
|
|
public List<string> ExcludedFiles { get; set; } = new List<string>();
|
|
public List<string> ExcludedDirectories { get; set; } = new List<string>();
|
|
public List<string> ExcludedPatterns { get; set; } = new List<string>();
|
|
public bool StopOnFirstError { get; set; } = false;
|
|
public Dictionary<string, object> OperationSettings { get; set; } = new Dictionary<string, object>();
|
|
public List<string> Errors { get; set; } = new List<string>();
|
|
}
|
|
|
|
public class BatchRefactoringResult
|
|
{
|
|
public int TotalFiles { get; set; }
|
|
public int SuccessfulFiles { get; set; }
|
|
public int FailedFiles { get; set; }
|
|
public DateTime StartTime { get; set; }
|
|
public DateTime EndTime { get; set; }
|
|
public TimeSpan TotalDuration { get; set; }
|
|
public bool StoppedOnError { get; set; }
|
|
public List<BatchFileRefactoringResult> FileResults { get; set; } = new List<BatchFileRefactoringResult>();
|
|
}
|
|
|
|
public class BatchFileRefactoringResult
|
|
{
|
|
public string FilePath { get; set; }
|
|
public string FileName { get; set; }
|
|
public bool Success { get; set; }
|
|
public string Error { get; set; }
|
|
public int TotalOperations { get; set; }
|
|
public int SuccessfulOperations { get; set; }
|
|
public DateTime StartTime { get; set; }
|
|
public DateTime EndTime { get; set; }
|
|
public TimeSpan Duration { get; set; }
|
|
public List<OperationResult> OperationResults { get; set; } = new List<OperationResult>();
|
|
}
|
|
|
|
public class OperationResult
|
|
{
|
|
public string OperationType { get; set; }
|
|
public bool Success { get; set; }
|
|
public string Message { get; set; }
|
|
public string Error { get; set; }
|
|
public object Details { get; set; }
|
|
public DateTime StartTime { get; set; }
|
|
public DateTime EndTime { get; set; }
|
|
public TimeSpan Duration { get; set; }
|
|
}
|
|
} |