333 lines
11 KiB
C#
Executable File
333 lines
11 KiB
C#
Executable File
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
|
|
namespace MarketAlly.AIPlugin.Refactoring.Plugins
|
|
{
|
|
// Unified exception hierarchy
|
|
public class RefactoringException : Exception
|
|
{
|
|
public string PluginName { get; }
|
|
public string Operation { get; }
|
|
public RefactoringErrorCode ErrorCode { get; }
|
|
public Dictionary<string, object> Context { get; }
|
|
|
|
public RefactoringException(string pluginName, string operation, RefactoringErrorCode errorCode, string message, Exception innerException = null)
|
|
: base($"[{pluginName}] {operation}: {message}", innerException)
|
|
{
|
|
PluginName = pluginName;
|
|
Operation = operation;
|
|
ErrorCode = errorCode;
|
|
Context = new Dictionary<string, object>();
|
|
}
|
|
|
|
public RefactoringException AddContext(string key, object value)
|
|
{
|
|
Context[key] = value;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
public enum RefactoringErrorCode
|
|
{
|
|
// General errors (1000-1999)
|
|
Unknown = 1000,
|
|
InvalidInput = 1001,
|
|
InvalidConfiguration = 1002,
|
|
OperationCancelled = 1003,
|
|
TimeoutExceeded = 1004,
|
|
|
|
// File system errors (2000-2999)
|
|
FileNotFound = 2000,
|
|
FileAccessDenied = 2001,
|
|
FileInUse = 2002,
|
|
DirectoryNotFound = 2003,
|
|
InvalidFilePath = 2004,
|
|
FileTooLarge = 2005,
|
|
|
|
// Code analysis errors (3000-3999)
|
|
ParseError = 3000,
|
|
SyntaxError = 3001,
|
|
SemanticError = 3002,
|
|
UnsupportedLanguageFeature = 3003,
|
|
ComplexityTooHigh = 3004,
|
|
|
|
// Git operation errors (4000-4999)
|
|
GitRepositoryNotFound = 4000,
|
|
GitOperationFailed = 4001,
|
|
GitConflict = 4002,
|
|
GitUncommittedChanges = 4003,
|
|
|
|
// Network/API errors (5000-5999)
|
|
NetworkError = 5000,
|
|
ApiKeyInvalid = 5001,
|
|
ApiRateLimitExceeded = 5002,
|
|
ApiResponseError = 5003,
|
|
|
|
// Security errors (6000-6999)
|
|
SecurityViolation = 6000,
|
|
UnauthorizedAccess = 6001,
|
|
CommandInjectionAttempt = 6002,
|
|
PathTraversalAttempt = 6003
|
|
}
|
|
|
|
// Error handling service interface
|
|
public interface IErrorHandlingService
|
|
{
|
|
RefactoringException CreateException(string pluginName, string operation, RefactoringErrorCode errorCode, string message, Exception innerException = null);
|
|
void LogError(RefactoringException exception, ILogger logger = null);
|
|
void LogWarning(string pluginName, string operation, string message, ILogger logger = null);
|
|
void LogInfo(string pluginName, string operation, string message, ILogger logger = null);
|
|
AIPluginResult CreateErrorResult(RefactoringException exception);
|
|
AIPluginResult CreateErrorResult(string pluginName, string operation, RefactoringErrorCode errorCode, string message, Exception innerException = null);
|
|
}
|
|
|
|
// Concrete error handling service
|
|
public class ErrorHandlingService : IErrorHandlingService
|
|
{
|
|
private readonly ILogger<ErrorHandlingService> _logger;
|
|
|
|
public ErrorHandlingService(ILogger<ErrorHandlingService> logger = null)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
public RefactoringException CreateException(string pluginName, string operation, RefactoringErrorCode errorCode, string message, Exception innerException = null)
|
|
{
|
|
return new RefactoringException(pluginName, operation, errorCode, message, innerException);
|
|
}
|
|
|
|
public void LogError(RefactoringException exception, ILogger logger = null)
|
|
{
|
|
var loggerToUse = logger ?? _logger;
|
|
|
|
if (loggerToUse != null)
|
|
{
|
|
using var scope = loggerToUse.BeginScope(new Dictionary<string, object>
|
|
{
|
|
["PluginName"] = exception.PluginName,
|
|
["Operation"] = exception.Operation,
|
|
["ErrorCode"] = exception.ErrorCode,
|
|
["Context"] = exception.Context
|
|
});
|
|
|
|
loggerToUse.LogError(exception, "Refactoring operation failed: {Message}", exception.Message);
|
|
}
|
|
}
|
|
|
|
public void LogWarning(string pluginName, string operation, string message, ILogger logger = null)
|
|
{
|
|
var loggerToUse = logger ?? _logger;
|
|
|
|
if (loggerToUse != null)
|
|
{
|
|
using var scope = loggerToUse.BeginScope(new Dictionary<string, object>
|
|
{
|
|
["PluginName"] = pluginName,
|
|
["Operation"] = operation
|
|
});
|
|
|
|
loggerToUse.LogWarning("Refactoring warning: {Message}", message);
|
|
}
|
|
}
|
|
|
|
public void LogInfo(string pluginName, string operation, string message, ILogger logger = null)
|
|
{
|
|
var loggerToUse = logger ?? _logger;
|
|
|
|
if (loggerToUse != null)
|
|
{
|
|
using var scope = loggerToUse.BeginScope(new Dictionary<string, object>
|
|
{
|
|
["PluginName"] = pluginName,
|
|
["Operation"] = operation
|
|
});
|
|
|
|
loggerToUse.LogInformation("Refactoring info: {Message}", message);
|
|
}
|
|
}
|
|
|
|
public AIPluginResult CreateErrorResult(RefactoringException exception)
|
|
{
|
|
LogError(exception);
|
|
|
|
return new AIPluginResult(exception, JsonSerializer.Serialize(new
|
|
{
|
|
Error = true,
|
|
ErrorCode = exception.ErrorCode,
|
|
PluginName = exception.PluginName,
|
|
Operation = exception.Operation,
|
|
Message = exception.Message,
|
|
Context = exception.Context,
|
|
Timestamp = DateTime.UtcNow
|
|
}));
|
|
}
|
|
|
|
public AIPluginResult CreateErrorResult(string pluginName, string operation, RefactoringErrorCode errorCode, string message, Exception innerException = null)
|
|
{
|
|
var exception = CreateException(pluginName, operation, errorCode, message, innerException);
|
|
return CreateErrorResult(exception);
|
|
}
|
|
}
|
|
|
|
// Extension methods for common exception scenarios
|
|
public static class ErrorHandlingExtensions
|
|
{
|
|
public static RefactoringException FileNotFound(this IErrorHandlingService service, string pluginName, string operation, string filePath)
|
|
{
|
|
return service.CreateException(pluginName, operation, RefactoringErrorCode.FileNotFound, $"File not found: {filePath}")
|
|
.AddContext("FilePath", filePath);
|
|
}
|
|
|
|
public static RefactoringException InvalidInput(this IErrorHandlingService service, string pluginName, string operation, string parameterName, string reason)
|
|
{
|
|
return service.CreateException(pluginName, operation, RefactoringErrorCode.InvalidInput, $"Invalid input for parameter '{parameterName}': {reason}")
|
|
.AddContext("ParameterName", parameterName)
|
|
.AddContext("Reason", reason);
|
|
}
|
|
|
|
public static RefactoringException ParseError(this IErrorHandlingService service, string pluginName, string operation, string filePath, Exception innerException)
|
|
{
|
|
return service.CreateException(pluginName, operation, RefactoringErrorCode.ParseError, $"Failed to parse file: {filePath}", innerException)
|
|
.AddContext("FilePath", filePath);
|
|
}
|
|
|
|
public static RefactoringException GitOperationFailed(this IErrorHandlingService service, string pluginName, string operation, string gitCommand, Exception innerException)
|
|
{
|
|
return service.CreateException(pluginName, operation, RefactoringErrorCode.GitOperationFailed, $"Git operation failed: {gitCommand}", innerException)
|
|
.AddContext("GitCommand", gitCommand);
|
|
}
|
|
|
|
public static RefactoringException SecurityViolation(this IErrorHandlingService service, string pluginName, string operation, string reason)
|
|
{
|
|
return service.CreateException(pluginName, operation, RefactoringErrorCode.SecurityViolation, $"Security violation: {reason}")
|
|
.AddContext("SecurityReason", reason);
|
|
}
|
|
|
|
public static RefactoringException ApiError(this IErrorHandlingService service, string pluginName, string operation, RefactoringErrorCode errorCode, string apiName, Exception innerException)
|
|
{
|
|
return service.CreateException(pluginName, operation, errorCode, $"API operation failed: {apiName}", innerException)
|
|
.AddContext("ApiName", apiName);
|
|
}
|
|
}
|
|
|
|
// Error recovery strategies
|
|
public interface IErrorRecoveryStrategy
|
|
{
|
|
bool CanRecover(RefactoringException exception);
|
|
Task<bool> TryRecoverAsync(RefactoringException exception);
|
|
}
|
|
|
|
public class FileAccessRecoveryStrategy : IErrorRecoveryStrategy
|
|
{
|
|
private readonly int _maxRetries;
|
|
private readonly int _retryDelayMs;
|
|
|
|
public FileAccessRecoveryStrategy(int maxRetries = 3, int retryDelayMs = 1000)
|
|
{
|
|
_maxRetries = maxRetries;
|
|
_retryDelayMs = retryDelayMs;
|
|
}
|
|
|
|
public bool CanRecover(RefactoringException exception)
|
|
{
|
|
return exception.ErrorCode == RefactoringErrorCode.FileInUse ||
|
|
exception.ErrorCode == RefactoringErrorCode.FileAccessDenied;
|
|
}
|
|
|
|
public async Task<bool> TryRecoverAsync(RefactoringException exception)
|
|
{
|
|
if (!CanRecover(exception))
|
|
return false;
|
|
|
|
for (int attempt = 1; attempt <= _maxRetries; attempt++)
|
|
{
|
|
await Task.Delay(_retryDelayMs * attempt);
|
|
|
|
// The actual recovery logic would depend on the specific operation
|
|
// For now, just return false to indicate recovery failed
|
|
// In practice, this would retry the file operation
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Centralized error handler with recovery strategies
|
|
public class CentralizedErrorHandler
|
|
{
|
|
private readonly IErrorHandlingService _errorService;
|
|
private readonly List<IErrorRecoveryStrategy> _recoveryStrategies;
|
|
|
|
public CentralizedErrorHandler(IErrorHandlingService errorService)
|
|
{
|
|
_errorService = errorService;
|
|
_recoveryStrategies = new List<IErrorRecoveryStrategy>
|
|
{
|
|
new FileAccessRecoveryStrategy()
|
|
};
|
|
}
|
|
|
|
public void AddRecoveryStrategy(IErrorRecoveryStrategy strategy)
|
|
{
|
|
_recoveryStrategies.Add(strategy);
|
|
}
|
|
|
|
public async Task<AIPluginResult> HandleErrorAsync(string pluginName, string operation, Exception exception)
|
|
{
|
|
RefactoringException refactoringException;
|
|
|
|
if (exception is RefactoringException rex)
|
|
{
|
|
refactoringException = rex;
|
|
}
|
|
else
|
|
{
|
|
// Convert generic exception to RefactoringException
|
|
var errorCode = DetermineErrorCode(exception);
|
|
refactoringException = _errorService.CreateException(pluginName, operation, errorCode, exception.Message, exception);
|
|
}
|
|
|
|
// Try recovery strategies
|
|
foreach (var strategy in _recoveryStrategies)
|
|
{
|
|
if (strategy.CanRecover(refactoringException))
|
|
{
|
|
if (await strategy.TryRecoverAsync(refactoringException))
|
|
{
|
|
_errorService.LogInfo(pluginName, operation, "Error recovered successfully");
|
|
return null; // Indicate recovery successful, operation can continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// No recovery possible, return error result
|
|
return _errorService.CreateErrorResult(refactoringException);
|
|
}
|
|
|
|
private RefactoringErrorCode DetermineErrorCode(Exception exception)
|
|
{
|
|
return exception switch
|
|
{
|
|
FileNotFoundException => RefactoringErrorCode.FileNotFound,
|
|
DirectoryNotFoundException => RefactoringErrorCode.DirectoryNotFound,
|
|
UnauthorizedAccessException => RefactoringErrorCode.FileAccessDenied,
|
|
ArgumentException => RefactoringErrorCode.InvalidInput,
|
|
TimeoutException => RefactoringErrorCode.TimeoutExceeded,
|
|
OperationCanceledException => RefactoringErrorCode.OperationCancelled,
|
|
_ => RefactoringErrorCode.Unknown
|
|
};
|
|
}
|
|
}
|
|
|
|
// Global error handler instance
|
|
public static class GlobalErrorHandler
|
|
{
|
|
private static readonly Lazy<CentralizedErrorHandler> _instance = new Lazy<CentralizedErrorHandler>(
|
|
() => new CentralizedErrorHandler(new ErrorHandlingService()));
|
|
|
|
public static CentralizedErrorHandler Instance => _instance.Value;
|
|
}
|
|
} |