blob: 72bd11c645bc6c1dad6d78531a60ec4cfc074801 [file]
use crate::utils::byte_size::IggyByteSize;
use crate::utils::topic_size::MaxTopicSize;
use strum::{EnumDiscriminants, FromRepr, IntoStaticStr};
use thiserror::Error;
#[derive(Clone, Debug, Error, EnumDiscriminants, IntoStaticStr, FromRepr)]
#[repr(u32)]
#[strum(serialize_all = "snake_case")]
#[strum_discriminants(
vis(pub(crate)),
derive(FromRepr, IntoStaticStr),
strum(serialize_all = "snake_case")
)]
pub enum IggyError {
#[error("Error")]
Error = 1,
#[error("Invalid configuration")]
InvalidConfiguration = 2,
#[error("Invalid command")]
InvalidCommand = 3,
#[error("Invalid format")]
InvalidFormat = 4,
#[error("Feature is unavailable")]
FeatureUnavailable = 5,
#[error("Invalid identifier")]
InvalidIdentifier = 6,
#[error("Invalid version: {0}")]
InvalidVersion(String) = 7,
#[error("Disconnected")]
Disconnected = 8,
#[error("Cannot establish connection")]
CannotEstablishConnection = 9,
#[error("Cannot create base directory, Path: {0}")]
CannotCreateBaseDirectory(String) = 10,
#[error("Cannot create runtime directory, Path: {0}")]
CannotCreateRuntimeDirectory(String) = 11,
#[error("Cannot remove runtime directory, Path: {0}")]
CannotRemoveRuntimeDirectory(String) = 12,
#[error("Cannot create state directory, Path: {0}")]
CannotCreateStateDirectory(String) = 13,
#[error("State file not found")]
StateFileNotFound = 14,
#[error("State file corrupted")]
StateFileCorrupted = 15,
#[error("Invalid state entry checksum: {0}, expected: {1}, for index: {2}")]
InvalidStateEntryChecksum(u32, u32, u64) = 16,
#[error("Cannot open database, Path: {0}")]
CannotOpenDatabase(String) = 19,
#[error("Resource with key: {0} was not found.")]
ResourceNotFound(String) = 20,
#[error("Stale client")]
StaleClient = 30,
#[error("TCP error")]
TcpError = 31,
#[error("QUIC error")]
QuicError = 32,
#[error("Invalid server address")]
InvalidServerAddress = 33,
#[error("Invalid client address")]
InvalidClientAddress = 34,
#[error("Unauthenticated")]
Unauthenticated = 40,
#[error("Unauthorized")]
Unauthorized = 41,
#[error("Invalid credentials")]
InvalidCredentials = 42,
#[error("Invalid username")]
InvalidUsername = 43,
#[error("Invalid password")]
InvalidPassword = 44,
#[error("Invalid user status")]
InvalidUserStatus = 45,
#[error("User already exists")]
UserAlreadyExists = 46,
#[error("User inactive")]
UserInactive = 47,
#[error("Cannot delete user with ID: {0}")]
CannotDeleteUser(u32) = 48,
#[error("Cannot change permissions for user with ID: {0}")]
CannotChangePermissions(u32) = 49,
#[error("Invalid personal access token name")]
InvalidPersonalAccessTokenName = 50,
#[error("Personal access token: {0} for user with ID: {1} already exists")]
PersonalAccessTokenAlreadyExists(String, u32) = 51,
#[error("User with ID: {0} has reached the maximum number of personal access tokens: {1}")]
PersonalAccessTokensLimitReached(u32, u32) = 52,
#[error("Invalid personal access token")]
InvalidPersonalAccessToken = 53,
#[error("Personal access token: {0} for user with ID: {1} has expired.")]
PersonalAccessTokenExpired(String, u32) = 54,
#[error("Users limit reached.")]
UsersLimitReached = 55,
#[error("Not connected")]
NotConnected = 61,
#[error("Client shutdown")]
ClientShutdown = 63,
#[error("Invalid TLS domain")]
InvalidTlsDomain = 64,
#[error("Invalid TLS certificate path")]
InvalidTlsCertificatePath = 65,
#[error("Invalid TLS certificate")]
InvalidTlsCertificate = 66,
#[error("Failed to add certificate")]
FailedToAddCertificate = 67,
#[error("Invalid encryption key")]
InvalidEncryptionKey = 70,
#[error("Cannot encrypt data")]
CannotEncryptData = 71,
#[error("Cannot decrypt data")]
CannotDecryptData = 72,
#[error("Invalid JWT algorithm: {0}")]
InvalidJwtAlgorithm(String) = 73,
#[error("Invalid JWT secret")]
InvalidJwtSecret = 74,
#[error("JWT is missing")]
JwtMissing = 75,
#[error("Cannot generate JWT")]
CannotGenerateJwt = 76,
#[error("Access token is missing")]
AccessTokenMissing = 77,
#[error("Invalid access token")]
InvalidAccessToken = 78,
#[error("Invalid size bytes")]
InvalidSizeBytes = 80,
#[error("Invalid UTF-8")]
InvalidUtf8 = 81,
#[error("Invalid number encoding")]
InvalidNumberEncoding = 82,
#[error("Invalid boolean value")]
InvalidBooleanValue,
#[error("Invalid number value")]
InvalidNumberValue,
#[error("Client with ID: {0} was not found.")]
ClientNotFound(u32) = 100,
#[error("Invalid client ID")]
InvalidClientId = 101,
#[error("Connection closed")]
ConnectionClosed = 206,
#[error("Cannot parse header kind from {0}")]
CannotParseHeaderKind(String) = 209,
#[error("HTTP response error, status: {0}, body: {1}")]
HttpResponseError(u16, String) = 300,
#[error("Invalid HTTP request")]
InvalidHttpRequest = 301,
#[error("Invalid JSON response")]
InvalidJsonResponse = 302,
#[error("Invalid bytes response")]
InvalidBytesResponse = 303,
#[error("Empty response")]
EmptyResponse = 304,
#[error("Cannot create endpoint")]
CannotCreateEndpoint = 305,
#[error("Cannot parse URL")]
CannotParseUrl = 306,
#[error("Cannot create streams directory, Path: {0}")]
CannotCreateStreamsDirectory(String) = 1000,
#[error("Cannot create stream with ID: {0} directory, Path: {1}")]
CannotCreateStreamDirectory(u32, String) = 1001,
#[error("Failed to create stream info file for stream with ID: {0}")]
CannotCreateStreamInfo(u32) = 1002,
#[error("Failed to update stream info for stream with ID: {0}")]
CannotUpdateStreamInfo(u32) = 1003,
#[error("Failed to open stream info file for stream with ID: {0}")]
CannotOpenStreamInfo(u32) = 1004,
#[error("Failed to read stream info file for stream with ID: {0}")]
CannotReadStreamInfo(u32) = 1005,
#[error("Failed to create stream with ID: {0}")]
CannotCreateStream(u32) = 1006,
#[error("Failed to delete stream with ID: {0}")]
CannotDeleteStream(u32) = 1007,
#[error("Failed to delete stream directory with ID: {0}")]
CannotDeleteStreamDirectory(u32) = 1008,
#[error("Stream with ID: {0} was not found.")]
StreamIdNotFound(u32) = 1009,
#[error("Stream with name: {0} was not found.")]
StreamNameNotFound(String) = 1010,
#[error("Stream with ID: {0} already exists.")]
StreamIdAlreadyExists(u32) = 1011,
#[error("Stream with name: {0} already exists.")]
StreamNameAlreadyExists(String) = 1012,
#[error("Invalid stream name")]
InvalidStreamName = 1013,
#[error("Invalid stream ID")]
InvalidStreamId = 1014,
#[error("Cannot read streams")]
CannotReadStreams = 1015,
#[error("Missing streams")]
MissingStreams = 1016,
#[error("Missing topics for stream with ID: {0}")]
MissingTopics(u32) = 1017,
#[error("Missing partitions for topic with ID: {0} for stream with ID: {1}.")]
MissingPartitions(u32, u32) = 1018,
#[error("Max topic size cannot be lower than segment size. Max topic size: {0} < segment size: {1}.")]
InvalidTopicSize(MaxTopicSize, IggyByteSize) = 1019,
#[error("Cannot create topics directory for stream with ID: {0}, Path: {1}")]
CannotCreateTopicsDirectory(u32, String) = 2000,
#[error(
"Failed to create directory for topic with ID: {0} for stream with ID: {1}, Path: {2}"
)]
CannotCreateTopicDirectory(u32, u32, String) = 2001,
#[error("Failed to create topic info file for topic with ID: {0} for stream with ID: {1}.")]
CannotCreateTopicInfo(u32, u32) = 2002,
#[error("Failed to update topic info for topic with ID: {0} for stream with ID: {1}.")]
CannotUpdateTopicInfo(u32, u32) = 2003,
#[error("Failed to open topic info file for topic with ID: {0} for stream with ID: {1}.")]
CannotOpenTopicInfo(u32, u32) = 2004,
#[error("Failed to read topic info file for topic with ID: {0} for stream with ID: {1}.")]
CannotReadTopicInfo(u32, u32) = 2005,
#[error("Failed to create topic with ID: {0} for stream with ID: {1}.")]
CannotCreateTopic(u32, u32) = 2006,
#[error("Failed to delete topic with ID: {0} for stream with ID: {1}.")]
CannotDeleteTopic(u32, u32) = 2007,
#[error("Failed to delete topic directory with ID: {0} for stream with ID: {1}, Path: {2}")]
CannotDeleteTopicDirectory(u32, u32, String) = 2008,
#[error("Cannot poll topic")]
CannotPollTopic = 2009,
#[error("Topic with ID: {0} for stream with ID: {1} was not found.")]
TopicIdNotFound(u32, u32) = 2010,
#[error("Topic with name: {0} for stream with ID: {1} was not found.")]
TopicNameNotFound(String, String) = 2011,
#[error("Topic with ID: {0} for stream with ID: {1} already exists.")]
TopicIdAlreadyExists(u32, u32) = 2012,
#[error("Topic with name: {0} for stream with ID: {1} already exists.")]
TopicNameAlreadyExists(String, u32) = 2013,
#[error("Invalid topic name")]
InvalidTopicName = 2014,
#[error("Too many partitions")]
TooManyPartitions = 2015,
#[error("Invalid topic ID")]
InvalidTopicId = 2016,
#[error("Cannot read topics for stream with ID: {0}")]
CannotReadTopics(u32) = 2017,
#[error("Invalid replication factor")]
InvalidReplicationFactor = 2018,
#[error("Cannot create partition with ID: {0} for stream with ID: {1} and topic with ID: {2}")]
CannotCreatePartition(u32, u32, u32) = 3000,
#[error(
"Failed to create directory for partitions for stream with ID: {0} and topic with ID: {1}"
)]
CannotCreatePartitionsDirectory(u32, u32) = 3001,
#[error("Failed to create directory for partition with ID: {0} for stream with ID: {1} and topic with ID: {2}")]
CannotCreatePartitionDirectory(u32, u32, u32) = 3002,
#[error("Cannot open partition log file")]
CannotOpenPartitionLogFile = 3003,
#[error("Cannot read partitions directories.")]
CannotReadPartitions = 3004,
#[error(
"Failed to delete partition with ID: {0} for stream with ID: {1} and topic with ID: {2}"
)]
CannotDeletePartition(u32, u32, u32) = 3005,
#[error("Failed to delete partition directory with ID: {0} for stream with ID: {1} and topic with ID: {2}")]
CannotDeletePartitionDirectory(u32, u32, u32) = 3006,
#[error(
"Partition with ID: {0} for topic with ID: {1} for stream with ID: {2} was not found."
)]
PartitionNotFound(u32, u32, u32) = 3007,
#[error("Topic with ID: {0} for stream with ID: {1} has no partitions.")]
NoPartitions(u32, u32) = 3008,
#[error("Cannot read partitions for topic with ID: {0} for stream with ID: {1}")]
TopicFull(u32, u32) = 3009,
#[error("Failed to delete consumer offsets directory for path: {0}")]
CannotDeleteConsumerOffsetsDirectory(String) = 3010,
#[error("Failed to delete consumer offset file for path: {0}")]
CannotDeleteConsumerOffsetFile(String) = 3011,
#[error("Failed to create consumer offsets directory for path: {0}")]
CannotCreateConsumerOffsetsDirectory(String) = 3012,
#[error("Failed to read consumers offsets from path: {0}")]
CannotReadConsumerOffsets(String) = 3020,
#[error("Consumer offset for consumer with ID: {0} was not found.")]
ConsumerOffsetNotFound(u32) = 3021,
#[error("Segment not found")]
SegmentNotFound = 4000,
#[error("Segment with start offset: {0} and partition with ID: {1} is closed")]
SegmentClosed(u64, u32) = 4001,
#[error("Segment size is invalid")]
InvalidSegmentSize(u64) = 4002,
#[error("Failed to create segment log file for Path: {0}.")]
CannotCreateSegmentLogFile(String) = 4003,
#[error("Failed to create segment index file for Path: {0}.")]
CannotCreateSegmentIndexFile(String) = 4004,
#[error("Failed to create segment time index file for Path: {0}.")]
CannotCreateSegmentTimeIndexFile(String) = 4005,
#[error("Cannot save messages to segment.")]
CannotSaveMessagesToSegment = 4006,
#[error("Cannot save index to segment.")]
CannotSaveIndexToSegment = 4007,
#[error("Cannot save time index to segment.")]
CannotSaveTimeIndexToSegment = 4008,
#[error("Invalid messages count")]
InvalidMessagesCount = 4009,
#[error("Cannot append message")]
CannotAppendMessage = 4010,
#[error("Cannot read message")]
CannotReadMessage = 4011,
#[error("Cannot read message ID")]
CannotReadMessageId = 4012,
#[error("Cannot read message state")]
CannotReadMessageState = 4013,
#[error("Cannot read message timestamp")]
CannotReadMessageTimestamp = 4014,
#[error("Cannot read headers length")]
CannotReadHeadersLength = 4015,
#[error("Cannot read headers payload")]
CannotReadHeadersPayload = 4016,
#[error("Too big headers payload")]
TooBigHeadersPayload = 4017,
#[error("Invalid header key")]
InvalidHeaderKey = 4018,
#[error("Invalid header value")]
InvalidHeaderValue = 4019,
#[error("Cannot read message length")]
CannotReadMessageLength = 4020,
#[error("Cannot save messages to segment")]
CannotReadMessagePayload = 4021,
#[error("Too big message payload")]
TooBigMessagePayload = 4022,
#[error("Too many messages")]
TooManyMessages = 4023,
#[error("Empty message payload")]
EmptyMessagePayload = 4024,
#[error("Invalid message payload length")]
InvalidMessagePayloadLength = 4025,
#[error("Cannot read message checksum")]
CannotReadMessageChecksum = 4026,
#[error("Invalid message checksum: {0}, expected: {1}, for offset: {2}")]
InvalidMessageChecksum(u32, u32, u64) = 4027,
#[error("Invalid key value length")]
InvalidKeyValueLength = 4028,
#[error("Command length error: {0}")]
CommandLengthError(String) = 4029,
#[error("Non-zero offset: {0} at index: {1}")]
NonZeroOffset(u64, u32) = 4030,
#[error("Non-zero timestamp: {0} at index: {1}")]
NonZeroTimestamp(u64, u32) = 4031,
#[error("Missing index: {0}")]
MissingIndex(u32) = 4032,
#[error("Invalid indexes byte size: {0}B, should be divisible by 16")]
InvalidIndexesByteSize(u32) = 4034,
#[error("Invalid indexes count: {0}, expected: {1}")]
InvalidIndexesCount(u32, u32) = 4035,
#[error("Invalid messages byte size: {0}B, expected: {1}B")]
InvalidMessagesSize(u32, u32) = 4036,
#[error("Cannot sed messages due to client disconnection")]
CannotSendMessagesDueToClientDisconnection = 4050,
#[error("Invalid offset: {0}")]
InvalidOffset(u64) = 4100,
#[error("Consumer group with ID: {0} for topic with ID: {1} was not found.")]
ConsumerGroupIdNotFound(u32, u32) = 5000,
#[error("Consumer group with ID: {0} for topic with ID: {1} already exists.")]
ConsumerGroupIdAlreadyExists(u32, u32) = 5001,
#[error("Invalid consumer group ID")]
InvalidConsumerGroupId = 5002,
#[error("Consumer group with name: {0} for topic with ID: {1} was not found.")]
ConsumerGroupNameNotFound(String, String) = 5003,
#[error("Consumer group with name: {0} for topic with ID: {1} already exists.")]
ConsumerGroupNameAlreadyExists(String, u32) = 5004,
#[error("Invalid consumer group name")]
InvalidConsumerGroupName = 5005,
#[error("Consumer group member with ID: {0} for group with ID: {1} for topic with ID: {2} was not found.")]
ConsumerGroupMemberNotFound(u32, u32, u32) = 5006,
#[error("Failed to create consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}.")]
CannotCreateConsumerGroupInfo(u32, u32, u32) = 5007,
#[error("Failed to delete consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}.")]
CannotDeleteConsumerGroupInfo(u32, u32, u32) = 5008,
#[error("Base offset is missing")]
MissingBaseOffsetRetainedMessageBatch = 6000,
#[error("Last offset delta is missing")]
MissingLastOffsetDeltaRetainedMessageBatch = 6001,
#[error("Max timestamp is missing")]
MissingMaxTimestampRetainedMessageBatch = 6002,
#[error("Length is missing")]
MissingLengthRetainedMessageBatch = 6003,
#[error("Payload is missing")]
MissingPayloadRetainedMessageBatch = 6004,
#[error("Cannot read batch base offset")]
CannotReadBatchBaseOffset = 7000,
#[error("Cannot read batch length")]
CannotReadBatchLength = 7001,
#[error("Cannot read batch last offset delta")]
CannotReadLastOffsetDelta = 7002,
#[error("Cannot read batch maximum timestamp")]
CannotReadMaxTimestamp = 7003,
#[error("Cannot read batch payload")]
CannotReadBatchPayload = 7004,
#[error("Invalid connection string")]
InvalidConnectionString = 8000,
#[error("Snapshot file completion failed")]
SnapshotFileCompletionFailed = 9000,
#[error("Cannot serialize resource")]
CannotSerializeResource = 10000,
#[error("Cannot deserialize resource")]
CannotDeserializeResource = 10001,
#[error("Cannot read file")]
CannotReadFile = 10002,
#[error("Cannot read file metadata")]
CannotReadFileMetadata = 10003,
#[error("Cannot seek file")]
CannotSeekFile = 10004,
#[error("Cannot append to file")]
CannotAppendToFile = 10005,
#[error("Cannot write to file")]
CannotWriteToFile = 10006,
#[error("Cannot overwrite file")]
CannotOverwriteFile = 10007,
#[error("Cannot delete file")]
CannotDeleteFile = 10008,
#[error("Cannot sync file")]
CannotSyncFile = 10009,
#[error("Cannot read index offset")]
CannotReadIndexOffset = 10010,
#[error("Cannot read index position")]
CannotReadIndexPosition = 10011,
#[error("Cannot read index timestamp")]
CannotReadIndexTimestamp = 10012,
}
impl IggyError {
pub fn as_code(&self) -> u32 {
// SAFETY: SdkError specifies #[repr(u32)] representation.
// https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting
unsafe { *(self as *const Self as *const u32) }
}
pub fn as_string(&self) -> &'static str {
self.into()
}
pub fn from_code(code: u32) -> Self {
IggyError::from_repr(code).unwrap_or(IggyError::Error)
}
pub fn from_code_as_string(code: u32) -> &'static str {
IggyErrorDiscriminants::from_repr(code)
.map(|discriminant| discriminant.into())
.unwrap_or("unknown error code")
}
}
impl PartialEq for IggyError {
fn eq(&self, other: &Self) -> bool {
self.as_code() == other.as_code()
}
}
#[cfg(test)]
mod tests {
use super::*;
const GROUP_NAME_ERROR_CODE: u32 = 5005;
#[test]
fn derived_sdk_error_discriminant_keeps_codes() {
assert_eq!(
GROUP_NAME_ERROR_CODE,
IggyError::InvalidConsumerGroupName.as_code()
);
assert_eq!(
GROUP_NAME_ERROR_CODE,
IggyErrorDiscriminants::InvalidConsumerGroupName as u32
);
}
#[test]
fn static_str_uses_kebab_case() {
assert_eq!(
"invalid_consumer_group_name",
IggyError::InvalidConsumerGroupName.as_string()
)
}
#[test]
fn gets_string_from_code() {
assert_eq!(
IggyError::InvalidConsumerGroupName.as_string(),
IggyError::from_code_as_string(GROUP_NAME_ERROR_CODE)
)
}
}