using System; using System.Collections; using System.Collections.Specialized; using System.Diagnostics; using System.Management.Automation; using System.Text; namespace PSMinIO.Utils { /// /// Centralized error handling utility for PSMinIO cmdlets /// Provides comprehensive error information including script details, line numbers, and context /// public static class MinIOErrorHandler { /// /// Handles and logs detailed error information for PSMinIO operations /// /// The cmdlet instance for logging /// The exception that occurred /// Name of the operation that failed /// Additional details about the operation /// PowerShell error category /// The target object related to the error public static void HandleError( PSCmdlet cmdlet, Exception exception, string operationName, string? operationDetails = null, ErrorCategory errorCategory = ErrorCategory.NotSpecified, object? targetObject = null) { if (cmdlet == null) throw new ArgumentNullException(nameof(cmdlet)); if (exception == null) throw new ArgumentNullException(nameof(exception)); if (string.IsNullOrEmpty(operationName)) throw new ArgumentException("Operation name cannot be null or empty", nameof(operationName)); // Create detailed error information var errorDetails = CreateDetailedErrorInfo(exception, operationName, operationDetails); // Log detailed error information LogDetailedError(cmdlet, errorDetails); // Create and throw PowerShell error record var errorRecord = CreateErrorRecord(exception, operationName, errorCategory, targetObject); cmdlet.ThrowTerminatingError(errorRecord); } /// /// Handles and logs detailed error information for PSMinIO operations (non-terminating) /// /// The cmdlet instance for logging /// The exception that occurred /// Name of the operation that failed /// Additional details about the operation /// PowerShell error category /// The target object related to the error public static void HandleNonTerminatingError( PSCmdlet cmdlet, Exception exception, string operationName, string? operationDetails = null, ErrorCategory errorCategory = ErrorCategory.NotSpecified, object? targetObject = null) { if (cmdlet == null) throw new ArgumentNullException(nameof(cmdlet)); if (exception == null) throw new ArgumentNullException(nameof(exception)); if (string.IsNullOrEmpty(operationName)) throw new ArgumentException("Operation name cannot be null or empty", nameof(operationName)); // Create detailed error information var errorDetails = CreateDetailedErrorInfo(exception, operationName, operationDetails); // Log detailed error information LogDetailedError(cmdlet, errorDetails); // Create and write PowerShell error record try { var errorRecord = CreateErrorRecord(exception, operationName, errorCategory, targetObject); cmdlet.WriteError(errorRecord); } catch (InvalidOperationException) { // Ignore threading errors - this means we're being called from a background thread // The error details are still captured in the exception } } /// /// Creates detailed error information dictionary /// private static OrderedDictionary CreateDetailedErrorInfo(Exception exception, string operationName, string? operationDetails) { var errorDetails = new OrderedDictionary(); // Basic error information errorDetails.Add("Operation", operationName); errorDetails.Add("Message", exception.Message); errorDetails.Add("ExceptionType", exception.GetType().FullName); // Inner exception information if (exception.InnerException != null) { errorDetails.Add("InnerExceptionType", exception.InnerException.GetType().FullName); errorDetails.Add("InnerExceptionMessage", exception.InnerException.Message); } return errorDetails; } /// /// Logs detailed error information using PowerShell's Write-Warning /// Only logs if called from the main PowerShell thread to avoid threading issues /// private static void LogDetailedError(PSCmdlet cmdlet, OrderedDictionary errorDetails) { try { var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"); // Log each error detail (reduced set) foreach (DictionaryEntry detail in errorDetails) { var key = detail.Key?.ToString() ?? "Unknown"; var value = detail.Value?.ToString() ?? "N/A"; // Truncate very long values for readability if (value.Length > 200) { value = value.Substring(0, 197) + "..."; } cmdlet.WriteWarning($"{timestamp} - {key}: {value}"); } } catch (InvalidOperationException) { // Ignore threading errors - this means we're being called from a background thread // The error details will still be included in the exception that gets thrown } } /// /// Creates a PowerShell ErrorRecord with appropriate categorization /// private static ErrorRecord CreateErrorRecord(Exception exception, string operationName, ErrorCategory errorCategory, object? targetObject) { // Determine error category if not specified if (errorCategory == ErrorCategory.NotSpecified) { errorCategory = DetermineErrorCategory(exception); } // Create error ID var errorId = $"PSMinIO.{operationName}.{exception.GetType().Name}"; return new ErrorRecord(exception, errorId, errorCategory, targetObject); } /// /// Determines appropriate PowerShell error category based on exception type /// private static ErrorCategory DetermineErrorCategory(Exception exception) { return exception switch { ArgumentNullException => ErrorCategory.InvalidArgument, ArgumentException => ErrorCategory.InvalidArgument, UnauthorizedAccessException => ErrorCategory.PermissionDenied, System.Net.Http.HttpRequestException => ErrorCategory.ConnectionError, TimeoutException => ErrorCategory.OperationTimeout, System.IO.FileNotFoundException => ErrorCategory.ObjectNotFound, System.IO.DirectoryNotFoundException => ErrorCategory.ObjectNotFound, System.IO.IOException => ErrorCategory.WriteError, InvalidOperationException => ErrorCategory.InvalidOperation, NotSupportedException => ErrorCategory.NotImplemented, _ => ErrorCategory.NotSpecified }; } /// /// Creates a formatted error message for operation failures /// /// Name of the failed operation /// Operation details /// The exception that occurred /// Formatted error message public static string CreateOperationErrorMessage(string operationName, string? details, Exception exception) { var message = new StringBuilder(); message.Append($"Operation failed: {operationName}"); if (!string.IsNullOrEmpty(details)) { message.Append($" - {details}"); } message.Append($": {exception.Message}"); return message.ToString(); } /// /// Validates common parameters and throws appropriate exceptions /// /// Bucket name to validate /// Object name to validate (optional) public static void ValidateParameters(string? bucketName, string? objectName = null) { if (string.IsNullOrWhiteSpace(bucketName)) { throw new ArgumentException("Bucket name cannot be null or empty", nameof(bucketName)); } if (objectName != null && string.IsNullOrWhiteSpace(objectName)) { throw new ArgumentException("Object name cannot be empty when specified", nameof(objectName)); } } } }