use crate::{
codec::FromRadix10,
database::{
DatabaseError,
client::postgres::{DbError, PostgresError, authentication::Authentication},
},
misc::{ConnectionState, bytes_rsplit1, bytes_split1, from_utf8_basic},
};
use core::any::type_name;
#[derive(Debug)]
pub(crate) struct Message<'bytes> {
pub(crate) tag: u8,
pub(crate) ty: MessageTy<'bytes>,
}
#[derive(Debug)]
pub(crate) enum MessageTy<'bytes> {
Authentication(Authentication<'bytes>),
BackendKeyData,
BindComplete,
CloseComplete,
CommandComplete(u32),
CopyData,
CopyDone,
CopyInResponse,
CopyOutResponse,
DataRow(u16),
EmptyQueryResponse,
NoData,
NoticeResponse,
NotificationResponse,
ParameterDescription(u16),
ParameterStatus(&'bytes [u8], &'bytes [u8]),
ParseComplete,
PortalSuspended,
ReadyForQuery,
RowDescription(u16, &'bytes [u8]),
}
impl<'bytes> TryFrom<(&mut ConnectionState, &'bytes [u8])> for MessageTy<'bytes> {
type Error = crate::Error;
#[inline]
fn try_from(from: (&mut ConnectionState, &'bytes [u8])) -> Result<Self, Self::Error> {
let rslt = match from.1 {
[b'1', ..] => Self::ParseComplete,
[b'2', ..] => Self::BindComplete,
[b'3', ..] => Self::CloseComplete,
[b'A', ..] => Self::NotificationResponse,
[b'C', _, _, _, _, rest @ ..] => {
let rows = bytes_rsplit1(rest, b' ')
.next()
.and_then(|el| {
if let [all_but_last @ .., _] = el {
u32::from_radix_10(all_but_last).ok()
} else {
None
}
})
.unwrap_or(0);
Self::CommandComplete(rows)
}
[b'D', _, _, _, _, a, b, ..] => Self::DataRow(u16::from_be_bytes([*a, *b])),
[b'E', _, _, _, _, rest @ ..] => {
*from.0 = ConnectionState::Closed;
return Err(DbError::try_from(from_utf8_basic(rest)?)?.into());
}
[b'G', ..] => Self::CopyInResponse,
[b'H', ..] => Self::CopyOutResponse,
[b'I', ..] => Self::EmptyQueryResponse,
[b'K', _, _, _, _, _a, _b, _c, _d, _e, _f, _g, _h] => Self::BackendKeyData,
[b'N', ..] => Self::NoticeResponse,
[b'R', _, _, _, _, rest @ ..] => Self::Authentication(rest.try_into()?),
[b'S', _, _, _, _, rest @ ..] => {
let rslt = || {
let mut iter = bytes_split1(rest, b'\0');
let name = iter.next()?;
let value = iter.next()?;
let _ = iter.next()?;
Some((name, value))
};
let (name, value) = rslt().ok_or(PostgresError::UnexpectedDatabaseMessageBytes)?;
Self::ParameterStatus(name, value)
}
[b'T', _, _, _, _, a, b, rest @ ..] => {
Self::RowDescription(u16::from_be_bytes([*a, *b]), rest)
}
[b'Z', _, _, _, _, _] => Self::ReadyForQuery,
[b'c', ..] => Self::CopyDone,
[b'd', ..] => Self::CopyData,
[b'n', ..] => Self::NoData,
[b's', ..] => Self::PortalSuspended,
[b't', _, _, _, _, a, b, ..] => Self::ParameterDescription(u16::from_be_bytes([*a, *b])),
_ => {
return Err(
DatabaseError::UnexpectedValueFromBytes { expected: type_name::<Self>() }.into(),
);
}
};
Ok(rslt)
}
}