use alloc::string::String;
#[derive(Clone, PartialEq, Eq, thiserror::Error)]
pub enum SdkError {
#[error("{words}")]
WorkerRefused {
code: String,
words: String,
},
#[error("push reception is already claimed")]
ReceiverAlreadyClaimed,
#[error("idle receive interrupted by its owner")]
ReadInterrupted,
#[error("connection error: {description}")]
Connection {
description: String,
},
#[error("protocol error: {description}")]
Protocol {
description: String,
},
#[error("serialization error: {description}")]
Serialization {
description: String,
},
#[error("type validation failed: {description}")]
TypeValidation {
description: String,
},
#[error("backpressure: {reason}")]
Backpressure {
reason: String,
},
#[error("publish unresolved: {description}")]
PublishUnresolved {
description: String,
},
#[error("conversation {conversation_id}: {description}")]
Conversation {
conversation_id: String,
description: String,
},
#[error("store error: {description}")]
Store {
description: String,
},
#[error(
"transport not connected: {operation} needs a connected transport; call \
RemoteConfig::connect_tcp or RemoteConfig::connect_websocket first"
)]
NotConnected {
operation: String,
},
#[error("{surface} is not wired ({seam}); {alternative}")]
Unwired {
surface: String,
seam: String,
alternative: String,
},
}
impl SdkError {
#[cfg(feature = "std")]
pub(crate) fn conversation_refusal(
conversation_id: u64,
reason_code: u16,
message: Option<String>,
) -> Self {
use alloc::{format, string::ToString};
use liminal::protocol::{WorkerRefusal, WorkerRefusalCode};
if let Some(code) = WorkerRefusalCode::from_reason_code(reason_code) {
let Some(words) = message else {
return Self::Protocol {
description: "worker_refusal_missing_words".to_string(),
};
};
return match WorkerRefusal::new(code.name(), &words) {
Ok(_) => Self::WorkerRefused {
code: code.name().to_string(),
words,
},
Err(error) => Self::Protocol {
description: error
.message()
.unwrap_or("worker_refusal_invalid_record")
.to_string(),
},
};
}
Self::Conversation {
conversation_id: conversation_id.to_string(),
description: format!(
"server rejected conversation {conversation_id} (reason {reason_code}): {}",
message.unwrap_or_else(|| "no detail".to_string())
),
}
}
}
impl core::fmt::Debug for SdkError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::WorkerRefused { code, words } => formatter
.debug_struct("WorkerRefused")
.field("code", code)
.field("words_bytes", &words.len())
.finish(),
Self::ReceiverAlreadyClaimed => formatter.write_str("ReceiverAlreadyClaimed"),
Self::ReadInterrupted => formatter.write_str("ReadInterrupted"),
Self::Connection { description } => formatter
.debug_struct("Connection")
.field("description_bytes", &description.len())
.finish(),
Self::Protocol { description } => formatter
.debug_struct("Protocol")
.field("description_bytes", &description.len())
.finish(),
Self::Serialization { description } => formatter
.debug_struct("Serialization")
.field("description_bytes", &description.len())
.finish(),
Self::TypeValidation { description } => formatter
.debug_struct("TypeValidation")
.field("description_bytes", &description.len())
.finish(),
Self::Backpressure { reason } => formatter
.debug_struct("Backpressure")
.field("reason_bytes", &reason.len())
.finish(),
Self::PublishUnresolved { description } => formatter
.debug_struct("PublishUnresolved")
.field("description_bytes", &description.len())
.finish(),
Self::Conversation {
conversation_id,
description,
} => formatter
.debug_struct("Conversation")
.field("conversation_id", conversation_id)
.field("description_bytes", &description.len())
.finish(),
Self::Store { description } => formatter
.debug_struct("Store")
.field("description_bytes", &description.len())
.finish(),
Self::NotConnected { operation } => formatter
.debug_struct("NotConnected")
.field("operation", operation)
.finish(),
Self::Unwired {
surface,
seam,
alternative,
} => formatter
.debug_struct("Unwired")
.field("surface", surface)
.field("seam", seam)
.field("alternative", alternative)
.finish(),
}
}
}