use crate::{transfer::Status, Address, ContextFinished};
extern crate alloc;
#[derive(Debug, thiserror::Error)]
pub enum DeserializerError {
#[error("got error: {0}")]
InvalidInput(String),
#[error("the delivery assignation is already ready")]
UnexpectedReady,
#[error("the payload could not be deserialized with the provided deserializer")]
CannotDeserialize,
}
pub type DeserializerFn =
unsafe extern "C" fn(
input: *const std::os::raw::c_char,
) -> Result<alloc::sync::Arc<dyn AbstractTransport>, DeserializerError>;
pub const DESERIALIZER_SYMBOL_NAME: &str = "deserialize_transport";
pub type DeliverTo = Vec<(Address, Status)>;
#[allow(clippy::module_name_repetitions)]
#[async_trait::async_trait]
pub trait AbstractTransport: erased_serde::Serialize + GetID + Send + Sync {
async fn deliver(
self: alloc::sync::Arc<Self>,
context: &ContextFinished,
rcpt_to: DeliverTo,
message: &[u8],
) -> DeliverTo;
#[must_use]
#[inline]
fn get_symbol() -> DeserializerFn
where
Self: Sized + serde::Deserialize<'static> + 'static,
{
<Self as AbstractTransport>::deserialize
}
#[allow(unsafe_code, improper_ctypes_definitions)]
#[inline]
unsafe extern "C" fn deserialize<'de>(
input: *const std::os::raw::c_char,
) -> Result<alloc::sync::Arc<dyn AbstractTransport>, DeserializerError>
where
Self: Sized + serde::Deserialize<'de> + 'static,
{
let input = std::ffi::CStr::from_ptr(input);
let input = match input.to_str() {
Ok(input) => input,
Err(e) => return Err(DeserializerError::InvalidInput(e.to_string())),
};
match serde_json::from_str::<Self>(input) {
Ok(input) => Ok(alloc::sync::Arc::new(input)),
Err(e) => Err(DeserializerError::InvalidInput(e.to_string())),
}
}
}
impl std::fmt::Debug for dyn AbstractTransport {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AbstractTransport")
.field(&self.get_id())
.finish()
}
}
erased_serde::serialize_trait_object!(AbstractTransport);
pub trait GetID
where
Self: erased_serde::Serialize,
{
#[inline]
#[allow(clippy::unwrap_used)]
fn get_id(&self) -> String {
let writer = Vec::with_capacity(128);
let mut ser = serde_json::Serializer::new(writer);
erased_serde::serialize(self, &mut ser).unwrap();
String::from_utf8(ser.into_inner()).unwrap()
}
}
impl std::hash::Hash for dyn AbstractTransport {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.get_id().hash(state);
}
}
impl PartialEq for dyn AbstractTransport {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.get_id() == other.get_id()
}
}
impl Eq for dyn AbstractTransport {}
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Debug, Clone, Eq, serde::Deserialize)]
#[serde(untagged)]
pub enum WrapperSerde {
Raw(String),
#[serde(skip_deserializing)]
Ready(alloc::sync::Arc<dyn AbstractTransport>),
}
impl serde::Serialize for WrapperSerde {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Raw(s) => serializer.serialize_str(s),
Self::Ready(value) => erased_serde::serialize(value.as_ref(), serializer),
}
}
}
impl PartialEq for WrapperSerde {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Raw(l0), Self::Raw(r0)) => l0 == r0,
(Self::Ready(l0), Self::Ready(r0)) => l0 == r0,
_ => false,
}
}
}
impl std::hash::Hash for WrapperSerde {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Raw(raw) => raw.hash(state),
Self::Ready(ready) => ready.hash(state),
}
}
}
impl WrapperSerde {
#[inline]
#[allow(clippy::expect_used, clippy::unwrap_in_result)]
pub fn to_ready(&self, deserializer: &[DeserializerFn]) -> Result<Self, DeserializerError> {
match self {
Self::Ready(_) => Err(DeserializerError::UnexpectedReady),
Self::Raw(raw) => {
let i = alloc::ffi::CString::new(raw.as_bytes()).expect("CString::new failed");
deserializer
.iter()
.enumerate()
.find_map(|(nbr, deserialize)| {
#[allow(unsafe_code)]
match unsafe { deserialize(i.as_ptr()) } {
Ok(transport) => {
tracing::trace!("({nbr}) deserialize transport successfully");
Some(transport)
}
Err(err) => {
tracing::trace!("({nbr}) could not deserialize transport: {err}");
None
}
}
})
.ok_or(DeserializerError::CannotDeserialize)
.map(Self::Ready)
}
}
}
#[inline]
#[must_use]
#[allow(clippy::panic)]
pub fn unwrap_ready(self) -> alloc::sync::Arc<dyn AbstractTransport> {
match self {
Self::Ready(transport) => transport,
Self::Raw(_) => panic!("cannot unwrap a raw delivery assignation"),
}
}
}