using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace HCFS.SDK;
///
/// Base exception for all HCFS SDK errors.
///
public class HCFSException : Exception
{
///
/// Gets the error code associated with this exception.
///
public string? ErrorCode { get; }
///
/// Gets additional error details.
///
public IReadOnlyDictionary? Details { get; }
///
/// Gets the HTTP status code if applicable.
///
public int? StatusCode { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSException(string message) : base(message)
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The error code.
public HCFSException(string message, string errorCode) : base(message)
{
ErrorCode = errorCode;
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The error code.
/// Additional error details.
/// HTTP status code.
public HCFSException(string message, string? errorCode, IReadOnlyDictionary? details, int? statusCode) : base(message)
{
ErrorCode = errorCode;
Details = details;
StatusCode = statusCode;
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The inner exception.
public HCFSException(string message, Exception innerException) : base(message, innerException)
{
}
///
/// Checks if this error should trigger a retry.
///
/// True if the error is retryable.
public virtual bool IsRetryable()
{
return StatusCode >= 500 || StatusCode == 429 ||
this is HCFSConnectionException ||
this is HCFSTimeoutException;
}
///
/// Checks if this error is temporary.
///
/// True if the error is temporary.
public virtual bool IsTemporary()
{
return StatusCode == 429 || StatusCode == 502 || StatusCode == 503 || StatusCode == 504 ||
this is HCFSTimeoutException ||
this is HCFSConnectionException;
}
}
///
/// Thrown when connection to HCFS API fails.
///
public class HCFSConnectionException : HCFSException
{
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSConnectionException(string message = "Failed to connect to HCFS API")
: base(message, "CONNECTION_FAILED")
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The inner exception.
public HCFSConnectionException(string message, Exception innerException)
: base(message, innerException)
{
}
}
///
/// Thrown when authentication fails.
///
public class HCFSAuthenticationException : HCFSException
{
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSAuthenticationException(string message = "Authentication failed")
: base(message, "AUTH_FAILED", null, 401)
{
}
}
///
/// Thrown when user lacks permissions for an operation.
///
public class HCFSAuthorizationException : HCFSException
{
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSAuthorizationException(string message = "Insufficient permissions")
: base(message, "INSUFFICIENT_PERMISSIONS", null, 403)
{
}
}
///
/// Thrown when a requested resource is not found.
///
public class HCFSNotFoundException : HCFSException
{
///
/// Gets the type of resource that was not found.
///
public string? ResourceType { get; }
///
/// Gets the ID of the resource that was not found.
///
public string? ResourceId { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSNotFoundException(string message = "Resource not found")
: base(message, "NOT_FOUND", null, 404)
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The type of resource.
/// The resource ID.
public HCFSNotFoundException(string message, string? resourceType, string? resourceId)
: base(message, "NOT_FOUND", null, 404)
{
ResourceType = resourceType;
ResourceId = resourceId;
}
///
/// Gets the error message with resource details.
///
public override string Message
{
get
{
var message = base.Message;
if (!string.IsNullOrEmpty(ResourceType))
{
message += $" (type: {ResourceType})";
}
if (!string.IsNullOrEmpty(ResourceId))
{
message += $" (id: {ResourceId})";
}
return message;
}
}
}
///
/// Thrown when request validation fails.
///
public class HCFSValidationException : ValidationException
{
///
/// Gets the validation error details.
///
public IReadOnlyList? ValidationErrors { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSValidationException(string message = "Request validation failed") : base(message)
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The validation error details.
public HCFSValidationException(string message, IReadOnlyList validationErrors)
: base(message)
{
ValidationErrors = validationErrors;
}
///
/// Gets the error message with validation details.
///
public override string Message
{
get
{
var message = base.Message;
if (ValidationErrors != null && ValidationErrors.Count > 0)
{
message += $" ({ValidationErrors.Count} validation issues)";
}
return message;
}
}
}
///
/// Validation error detail.
///
public record ValidationErrorDetail
{
///
/// Gets the field name that failed validation.
///
[JsonPropertyName("field")]
public string? Field { get; init; }
///
/// Gets the validation error message.
///
[JsonPropertyName("message")]
public string Message { get; init; } = string.Empty;
///
/// Gets the validation error code.
///
[JsonPropertyName("code")]
public string? Code { get; init; }
}
///
/// Thrown when rate limit is exceeded.
///
public class HCFSRateLimitException : HCFSException
{
///
/// Gets the time to wait before retrying.
///
public double? RetryAfterSeconds { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSRateLimitException(string message = "Rate limit exceeded")
: base(message, "RATE_LIMIT_EXCEEDED", null, 429)
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// Seconds to wait before retrying.
public HCFSRateLimitException(string message, double? retryAfterSeconds)
: base(BuildMessage(message, retryAfterSeconds), "RATE_LIMIT_EXCEEDED", null, 429)
{
RetryAfterSeconds = retryAfterSeconds;
}
private static string BuildMessage(string message, double? retryAfterSeconds)
{
if (retryAfterSeconds.HasValue)
{
return $"{message}. Retry after {retryAfterSeconds.Value} seconds";
}
return message;
}
}
///
/// Thrown for server-side errors (5xx status codes).
///
public class HCFSServerException : HCFSException
{
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The HTTP status code.
public HCFSServerException(string message = "Internal server error", int statusCode = 500)
: base(message, "SERVER_ERROR", null, statusCode)
{
}
///
/// Gets the error message with status code.
///
public override string Message => $"Server error (HTTP {StatusCode}): {base.Message}";
}
///
/// Thrown when a request times out.
///
public class HCFSTimeoutException : HCFSException
{
///
/// Gets the timeout duration that was exceeded.
///
public TimeSpan? Timeout { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSTimeoutException(string message = "Request timed out")
: base(message, "TIMEOUT")
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The timeout duration.
public HCFSTimeoutException(string message, TimeSpan timeout)
: base($"{message} after {timeout.TotalMilliseconds}ms", "TIMEOUT")
{
Timeout = timeout;
}
}
///
/// Thrown for cache-related errors.
///
public class HCFSCacheException : HCFSException
{
///
/// Gets the cache operation that failed.
///
public string? Operation { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSCacheException(string message = "Cache operation failed")
: base(message, "CACHE_ERROR")
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The cache operation.
public HCFSCacheException(string message, string operation)
: base(message, "CACHE_ERROR")
{
Operation = operation;
}
///
/// Gets the error message with operation details.
///
public override string Message
{
get
{
if (!string.IsNullOrEmpty(Operation))
{
return $"Cache error during {Operation}: {base.Message}";
}
return $"Cache error: {base.Message}";
}
}
}
///
/// Thrown for batch operation errors.
///
public class HCFSBatchException : HCFSException
{
///
/// Gets the items that failed in the batch operation.
///
public IReadOnlyList? FailedItems { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSBatchException(string message = "Batch operation failed")
: base(message, "BATCH_ERROR")
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The failed items.
public HCFSBatchException(string message, IReadOnlyList failedItems)
: base(message, "BATCH_ERROR")
{
FailedItems = failedItems;
}
///
/// Gets the error message with failure details.
///
public override string Message
{
get
{
var message = base.Message;
if (FailedItems != null && FailedItems.Count > 0)
{
message += $" ({FailedItems.Count} failed items)";
}
return message;
}
}
}
///
/// Batch operation failure item.
///
public record BatchFailureItem
{
///
/// Gets the index of the failed item.
///
[JsonPropertyName("index")]
public int Index { get; init; }
///
/// Gets the error message for the failed item.
///
[JsonPropertyName("error")]
public string Error { get; init; } = string.Empty;
///
/// Gets the item data that failed.
///
[JsonPropertyName("item")]
public object? Item { get; init; }
}
///
/// Thrown for search operation errors.
///
public class HCFSSearchException : HCFSException
{
///
/// Gets the search query that failed.
///
public string? Query { get; }
///
/// Gets the search type that was used.
///
public string? SearchType { get; }
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSSearchException(string message = "Search failed")
: base(message, "SEARCH_ERROR")
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The search query.
/// The search type.
public HCFSSearchException(string message, string? query, string? searchType)
: base(message, "SEARCH_ERROR")
{
Query = query;
SearchType = searchType;
}
///
/// Gets the error message with search details.
///
public override string Message
{
get
{
var message = $"Search error: {base.Message}";
if (!string.IsNullOrEmpty(SearchType))
{
message += $" (type: {SearchType})";
}
if (!string.IsNullOrEmpty(Query))
{
message += $" (query: '{Query}')";
}
return message;
}
}
}
///
/// Thrown for streaming/WebSocket errors.
///
public class HCFSStreamException : HCFSException
{
///
/// Initializes a new instance of the class.
///
/// The error message.
public HCFSStreamException(string message = "Stream operation failed")
: base(message, "STREAM_ERROR")
{
}
///
/// Initializes a new instance of the class.
///
/// The error message.
/// The inner exception.
public HCFSStreamException(string message, Exception innerException)
: base(message, innerException)
{
}
}
///
/// Error response from the API.
///
internal record ApiErrorResponse
{
[JsonPropertyName("error")]
public string? Error { get; init; }
[JsonPropertyName("message")]
public string? Message { get; init; }
[JsonPropertyName("details")]
public Dictionary? Details { get; init; }
}