use std::ffi::CStr;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use anyhow::bail;
use anyhow::Result;
use futures::future::FutureExt;
use crate::serialize;
use crate::ApplicationException;
use crate::ContextStack;
use crate::Deserialize;
use crate::MessageType;
use crate::Protocol;
use crate::ProtocolEncodedFinal;
use crate::ProtocolReader;
use crate::ProtocolWriter;
use crate::RequestContext;
use crate::ResultInfo;
use crate::ResultType;
use crate::Serialize;
use crate::SerializedMessage;
pub fn enum_display(
variants_by_number: &[(&str, i32)],
formatter: &mut fmt::Formatter,
number: i32,
) -> fmt::Result {
match variants_by_number.binary_search_by_key(&number, |entry| entry.1) {
Ok(i) => formatter.write_str(variants_by_number[i].0),
Err(_) => write!(formatter, "{}", number),
}
}
pub fn enum_from_str(
variants_by_name: &[(&str, i32)],
value: &str,
type_name: &'static str,
) -> Result<i32> {
match variants_by_name.binary_search_by_key(&value, |entry| entry.0) {
Ok(i) => Ok(variants_by_name[i].1),
Err(_) => bail!("Unable to parse {} as {}", value, type_name),
}
}
pub fn type_name_of_val<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
pub fn serialize_result_envelope<P, CTXT, RES>(
name: &str,
name_cstr: &<CTXT::ContextStack as ContextStack>::Name,
seqid: u32,
rctxt: &CTXT,
ctx_stack: &mut CTXT::ContextStack,
res: RES,
) -> anyhow::Result<ProtocolEncodedFinal<P>>
where
P: Protocol,
RES: ResultInfo + Serialize<P::Sizer> + Serialize<P::Serializer>,
CTXT: RequestContext<Name = CStr>,
{
let res_type = res.result_type();
if matches!(res_type, ResultType::Error | ResultType::Exception) {
assert_eq!(res.exn_is_declared(), res_type == ResultType::Error);
rctxt.set_user_exception_header(res.exn_name(), &res.exn_value())?;
}
ctx_stack.pre_write()?;
let envelope = serialize!(P, |p| {
p.write_message_begin(name, res_type.message_type(), seqid);
res.write(p);
p.write_message_end();
});
ctx_stack.on_write_data(&SerializedMessage {
protocol: P::PROTOCOL_ID,
method_name: name_cstr,
buffer: PhantomData,
})?;
ctx_stack.post_write(0)?;
Ok(envelope)
}
pub fn serialize_stream_item<P, RES>(res: RES) -> anyhow::Result<ProtocolEncodedFinal<P>>
where
P: Protocol,
RES: ResultInfo + Serialize<P::Sizer> + Serialize<P::Serializer>,
{
Ok(serialize!(P, |p| {
res.write(p);
}))
}
pub fn serialize_request_envelope<P, ARGS>(
name: &str,
args: &ARGS,
) -> anyhow::Result<ProtocolEncodedFinal<P>>
where
P: Protocol,
ARGS: Serialize<P::Sizer> + Serialize<P::Serializer>,
{
let envelope = serialize!(P, |p| {
p.write_message_begin(name, MessageType::Call, 0);
args.write(p);
p.write_message_end();
});
Ok(envelope)
}
pub fn deserialize_response_envelope<P, T>(
de: &mut P::Deserializer,
) -> anyhow::Result<Result<T, ApplicationException>>
where
P: Protocol,
T: Deserialize<P::Deserializer>,
{
let (_, message_type, _) = de.read_message_begin(|_| ())?;
let res = match message_type {
MessageType::Reply => Ok(T::read(de)?),
MessageType::Exception => Err(ApplicationException::read(de)?),
MessageType::Call | MessageType::Oneway | MessageType::InvalidMessageType => {
bail!("Unwanted message type `{:?}`", message_type)
}
};
de.read_message_end()?;
Ok(res)
}
pub trait Spawner: 'static {
fn spawn<F, R>(func: F) -> Pin<Box<dyn Future<Output = R> + Send>>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static;
}
pub struct NoopSpawner;
impl Spawner for NoopSpawner {
#[inline]
fn spawn<F, R>(func: F) -> Pin<Box<dyn Future<Output = R> + Send>>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
async { func() }.boxed()
}
}
pub async fn async_deserialize_response_envelope<P, T, S>(
de: P::Deserializer,
) -> anyhow::Result<(Result<T, ApplicationException>, P::Deserializer)>
where
P: Protocol,
P::Deserializer: Send,
T: Deserialize<P::Deserializer> + Send + 'static,
S: Spawner,
{
S::spawn(move || {
let mut de = de;
let res = deserialize_response_envelope::<P, T>(&mut de);
res.map(|res| (res, de))
})
.await
}