use thiserror::Error;
use crate::types::SchemaType;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("schema manifest not found")]
ManifestNotFound,
#[error("schema not found")]
SchemaNotFound,
#[error("invalid manifest format: {0}")]
InvalidManifest(String),
#[error("invalid schema format: {0}")]
InvalidSchema(String),
#[error("schema exceeds size limit: {size} bytes (max {max_size})")]
SchemaToLarge { size: usize, max_size: usize },
#[error("schema checksum mismatch: expected {expected}, got {actual}")]
ChecksumMismatch { expected: String, actual: String },
#[error("unsupported schema type: {0}")]
UnsupportedType(SchemaType),
#[error("backend unavailable: {0}")]
BackendUnavailable(String),
#[error("incompatible protocol version: manifest version {manifest_version}, protocol version {protocol_version}")]
IncompatibleVersion {
manifest_version: String,
protocol_version: String,
},
#[error("invalid schema location: {0}")]
InvalidLocation(String),
#[error("schema provider not found for type: {0}")]
ProviderNotFound(SchemaType),
#[error("schema registry not configured")]
RegistryNotConfigured,
#[error("failed to fetch schema: {0}")]
SchemaFetchFailed(String),
#[error("schema validation failed: {0}")]
ValidationFailed(String),
#[error("{kind} not found: {id}")]
NotFound { kind: String, id: String },
#[error("registration failed: {0}")]
RegistrationFailed(String),
#[error("deregistration failed: {0}")]
DeregistrationFailed(String),
#[error("health check failed: {0}")]
HealthCheckFailed(String),
#[error("discovery backend unavailable: {0}")]
DiscoveryUnavailable(String),
#[error("manifest fetch failed: {0}")]
ManifestFetchFailed(String),
#[error("manifest error for service={service_name} instance={instance_id}: {source}")]
Manifest {
service_name: String,
instance_id: String,
#[source]
source: Box<Error>,
},
#[error("schema error type={schema_type} path={path}: {source}")]
Schema {
schema_type: SchemaType,
path: String,
#[source]
source: Box<Error>,
},
#[error("validation error: field={field} message={message}")]
Validation { field: String, message: String },
#[error("serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("channel send error")]
ChannelSend,
#[error("channel receive error")]
ChannelReceive,
#[error("custom error: {0}")]
Custom(String),
}
impl Error {
pub fn manifest(service_name: String, instance_id: String, source: Error) -> Self {
Error::Manifest {
service_name,
instance_id,
source: Box::new(source),
}
}
pub fn schema(schema_type: SchemaType, path: String, source: Error) -> Self {
Error::Schema {
schema_type,
path,
source: Box::new(source),
}
}
pub fn validation(field: impl Into<String>, message: impl Into<String>) -> Self {
Error::Validation {
field: field.into(),
message: message.into(),
}
}
pub fn invalid_manifest(message: impl Into<String>) -> Self {
Error::InvalidManifest(message.into())
}
pub fn invalid_schema(message: impl Into<String>) -> Self {
Error::InvalidSchema(message.into())
}
pub fn schema_too_large(size: usize, max_size: usize) -> Self {
Error::SchemaToLarge { size, max_size }
}
pub fn checksum_mismatch(expected: String, actual: String) -> Self {
Error::ChecksumMismatch { expected, actual }
}
pub fn incompatible_version(manifest_version: String, protocol_version: String) -> Self {
Error::IncompatibleVersion {
manifest_version,
protocol_version,
}
}
pub fn invalid_location(message: impl Into<String>) -> Self {
Error::InvalidLocation(message.into())
}
pub fn backend_unavailable(message: impl Into<String>) -> Self {
Error::BackendUnavailable(message.into())
}
pub fn schema_fetch_failed(message: impl Into<String>) -> Self {
Error::SchemaFetchFailed(message.into())
}
pub fn not_found(kind: impl Into<String>, id: impl Into<String>) -> Self {
Error::NotFound {
kind: kind.into(),
id: id.into(),
}
}
pub fn registration_failed(message: impl Into<String>) -> Self {
Error::RegistrationFailed(message.into())
}
pub fn discovery_unavailable(message: impl Into<String>) -> Self {
Error::DiscoveryUnavailable(message.into())
}
pub fn validation_failed(message: impl Into<String>) -> Self {
Error::ValidationFailed(message.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = Error::ManifestNotFound;
assert_eq!(err.to_string(), "schema manifest not found");
let err = Error::SchemaNotFound;
assert_eq!(err.to_string(), "schema not found");
let err = Error::invalid_manifest("test error");
assert_eq!(err.to_string(), "invalid manifest format: test error");
}
#[test]
fn test_validation_error() {
let err = Error::validation("field_name", "field is required");
assert!(err.to_string().contains("field_name"));
assert!(err.to_string().contains("field is required"));
}
#[test]
fn test_checksum_mismatch() {
let err = Error::checksum_mismatch("abc123".to_string(), "def456".to_string());
assert!(err.to_string().contains("abc123"));
assert!(err.to_string().contains("def456"));
}
}