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));
}
}
}
}