use crate::error_code::ErrorCode;
use crate::util::copy_to_region;
use crate::util::DisplayAsHexBytesLimitted;
use std::borrow::Cow;
use tarantool::error::BoxError;
use tarantool::error::TarantoolErrorCode;
use tarantool::unwrap_ok_or;
pub mod client;
pub mod server;
pub use client::RequestBuilder;
pub use client::RequestTarget;
pub use server::RouteBuilder;
#[derive(Clone)]
pub struct Request<'a> {
raw: Cow<'a, [u8]>,
}
impl<'a> Request<'a> {
#[inline(always)]
pub fn from_bytes(bytes: &'a [u8]) -> Self {
Self { raw: bytes.into() }
}
#[inline]
#[track_caller]
pub fn encode_rmp<T>(v: &T) -> Result<Self, BoxError>
where
T: serde::Serialize,
{
let bytes = unwrap_ok_or!(rmp_serde::encode::to_vec_named(v),
Err(e) => {
return Err(BoxError::new(ErrorCode::Other, e.to_string()));
}
);
Ok(Self { raw: bytes.into() })
}
#[inline]
#[track_caller]
pub fn encode_rmp_unnamed<T>(v: &T) -> Result<Self, BoxError>
where
T: serde::Serialize,
{
let bytes = unwrap_ok_or!(rmp_serde::encode::to_vec(v),
Err(e) => {
return Err(BoxError::new(ErrorCode::Other, e.to_string()));
}
);
Ok(Self { raw: bytes.into() })
}
#[inline(always)]
pub fn as_bytes(&'a self) -> &'a [u8] {
&self.raw
}
#[inline(always)]
#[track_caller]
pub fn decode_rmp<T>(&'a self) -> Result<T, BoxError>
where
T: serde::Deserialize<'a>,
{
match rmp_serde::from_slice(self.as_bytes()) {
Ok(r) => Ok(r),
Err(e) => {
Err(BoxError::new(
TarantoolErrorCode::InvalidMsgpack,
e.to_string(),
))
}
}
}
}
impl AsRef<[u8]> for Request<'_> {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl std::fmt::Debug for Request<'_> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&DisplayAsHexBytesLimitted(self.as_bytes()), f)
}
}
#[derive(Clone)]
pub struct Response {
inner: ResponseImpl,
}
#[derive(Debug, Clone)]
enum ResponseImpl {
RegionSlice(&'static [u8]),
Owned(Box<[u8]>),
}
impl Default for Response {
#[inline(always)]
fn default() -> Self {
Self::empty()
}
}
impl Response {
#[inline(always)]
pub(crate) fn new_owned(bytes: &[u8]) -> Self {
Self {
inner: ResponseImpl::Owned(bytes.into()),
}
}
pub(crate) fn to_region_slice(&self) -> Result<&'static [u8], BoxError> {
match &self.inner {
ResponseImpl::RegionSlice(region_slice) => Ok(region_slice),
ResponseImpl::Owned(boxed_slice) => copy_to_region(boxed_slice),
}
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
match &self.inner {
ResponseImpl::RegionSlice(region_slice) => region_slice,
ResponseImpl::Owned(boxed_slice) => boxed_slice,
}
}
#[inline(always)]
#[track_caller]
pub fn decode_rmp<'a, T>(&'a self) -> Result<T, BoxError>
where
T: serde::Deserialize<'a>,
{
match rmp_serde::from_slice(self.as_bytes()) {
Ok(r) => Ok(r),
Err(e) => {
Err(BoxError::new(
TarantoolErrorCode::InvalidMsgpack,
e.to_string(),
))
}
}
}
#[inline(always)]
#[track_caller]
pub fn from_bytes(bytes: &[u8]) -> Result<Self, BoxError> {
let region_slice = unwrap_ok_or!(copy_to_region(bytes),
Err(e) => {
return Err(BoxError::new(e.error_code(), e.message()));
}
);
Ok(Self {
inner: ResponseImpl::RegionSlice(region_slice),
})
}
#[inline(always)]
pub fn empty() -> Self {
Self::from_static(b"")
}
#[inline(always)]
pub fn from_static(static_slice: &'static [u8]) -> Self {
Self {
inner: ResponseImpl::RegionSlice(static_slice),
}
}
#[inline]
#[track_caller]
pub fn encode_rmp<T>(v: &T) -> Result<Self, BoxError>
where
T: serde::Serialize,
{
let res = rmp_serde::encode::to_vec_named(v);
let data = match res {
Ok(v) => v,
Err(e) => {
return Err(BoxError::new(ErrorCode::Other, e.to_string()));
}
};
Ok(Self {
inner: ResponseImpl::Owned(data.into()),
})
}
#[inline]
#[track_caller]
pub fn encode_rmp_unnamed<T>(v: &T) -> Result<Self, BoxError>
where
T: serde::Serialize,
{
let res = rmp_serde::encode::to_vec(v);
let data = match res {
Ok(v) => v,
Err(e) => {
return Err(BoxError::new(ErrorCode::Other, e.to_string()));
}
};
Ok(Self {
inner: ResponseImpl::Owned(data.into()),
})
}
}
impl std::fmt::Debug for Response {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Response::")?;
match self.inner {
ResponseImpl::Owned { .. } => f.write_str("Owned(")?,
ResponseImpl::RegionSlice { .. } => f.write_str("RegionSlice(")?,
}
std::fmt::Display::fmt(&DisplayAsHexBytesLimitted(self.as_bytes()), f)?;
f.write_str(")")?;
Ok(())
}
}
impl AsRef<[u8]> for Response {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl TryFrom<&[u8]> for Response {
type Error = BoxError;
#[inline(always)]
fn try_from(bytes: &[u8]) -> Result<Self, BoxError> {
Self::from_bytes(bytes)
}
}
#[cfg(all(feature = "internal_test", not(test)))]
mod tests {
use super::*;
#[tarantool::test]
fn check_error_location() {
let request = Request::from_bytes(b"\xa3foo");
let e = request.decode_rmp::<i32>().unwrap_err();
let error_line = line!() - 1;
assert_eq!(e.file(), Some(file!()));
assert_eq!(e.line(), Some(error_line));
let s: String = request.decode_rmp().unwrap();
assert_eq!(s, "foo");
let response = Response::from_static(b"\xa3foo");
let e = response.decode_rmp::<i32>().unwrap_err();
let error_line = line!() - 1;
assert_eq!(e.file(), Some(file!()));
assert_eq!(e.line(), Some(error_line));
let s: String = response.decode_rmp().unwrap();
assert_eq!(s, "foo");
}
}