use std::marker::PhantomData;
use crate::connection::{BufWithFds, RequestConnection, RequestKind};
use crate::errors::{ConnectionError, ReplyError};
#[cfg(feature = "record")]
use crate::protocol::record::EnableContextReply;
use crate::protocol::xproto::ListFontsWithInfoReply;
use crate::x11_utils::{TryParse, TryParseFd};
use x11rb_protocol::{DiscardMode, SequenceNumber};
#[derive(Debug)]
pub struct VoidCookie<'a, C>
where
C: RequestConnection + ?Sized,
{
connection: &'a C,
sequence_number: SequenceNumber,
}
impl<'a, C> VoidCookie<'a, C>
where
C: RequestConnection + ?Sized,
{
pub fn new(connection: &C, sequence_number: SequenceNumber) -> VoidCookie<'_, C> {
VoidCookie {
connection,
sequence_number,
}
}
pub fn sequence_number(&self) -> SequenceNumber {
self.sequence_number
}
fn consume(self) -> (&'a C, SequenceNumber) {
let result = (self.connection, self.sequence_number);
std::mem::forget(self);
result
}
pub fn check(self) -> Result<(), ReplyError> {
let (connection, sequence) = self.consume();
connection.check_for_error(sequence)
}
pub fn ignore_error(self) {
let (connection, sequence) = self.consume();
connection.discard_reply(
sequence,
RequestKind::IsVoid,
DiscardMode::DiscardReplyAndError,
)
}
pub(crate) fn replace_connection<C2: RequestConnection + ?Sized>(
self,
connection: &C2,
) -> VoidCookie<'_, C2> {
let (_, sequence_number) = self.consume();
VoidCookie {
connection,
sequence_number,
}
}
}
impl<C> Drop for VoidCookie<'_, C>
where
C: RequestConnection + ?Sized,
{
fn drop(&mut self) {
self.connection.discard_reply(
self.sequence_number,
RequestKind::IsVoid,
DiscardMode::DiscardReply,
)
}
}
#[derive(Debug)]
struct RawCookie<'a, C>
where
C: RequestConnection + ?Sized,
{
connection: &'a C,
sequence_number: SequenceNumber,
}
impl<C> RawCookie<'_, C>
where
C: RequestConnection + ?Sized,
{
fn new(connection: &C, sequence_number: SequenceNumber) -> RawCookie<'_, C> {
RawCookie {
connection,
sequence_number,
}
}
fn into_sequence_number(self) -> SequenceNumber {
let number = self.sequence_number;
std::mem::forget(self);
number
}
fn replace_connection<C2: RequestConnection + ?Sized>(
self,
connection: &C2,
) -> RawCookie<'_, C2> {
RawCookie {
connection,
sequence_number: self.into_sequence_number(),
}
}
}
impl<C> Drop for RawCookie<'_, C>
where
C: RequestConnection + ?Sized,
{
fn drop(&mut self) {
self.connection.discard_reply(
self.sequence_number,
RequestKind::HasResponse,
DiscardMode::DiscardReply,
);
}
}
#[derive(Debug)]
pub struct Cookie<'a, C, R>
where
C: RequestConnection + ?Sized,
{
raw_cookie: RawCookie<'a, C>,
phantom: PhantomData<R>,
}
impl<C, R> Cookie<'_, C, R>
where
R: TryParse,
C: RequestConnection + ?Sized,
{
pub fn new(connection: &C, sequence_number: SequenceNumber) -> Cookie<'_, C, R> {
Cookie {
raw_cookie: RawCookie::new(connection, sequence_number),
phantom: PhantomData,
}
}
pub fn sequence_number(&self) -> SequenceNumber {
self.raw_cookie.sequence_number
}
pub fn raw_reply(self) -> Result<C::Buf, ReplyError> {
let conn = self.raw_cookie.connection;
conn.wait_for_reply_or_error(self.raw_cookie.into_sequence_number())
}
pub fn raw_reply_unchecked(self) -> Result<Option<C::Buf>, ConnectionError> {
let conn = self.raw_cookie.connection;
conn.wait_for_reply(self.raw_cookie.into_sequence_number())
}
pub fn reply(self) -> Result<R, ReplyError> {
Ok(R::try_parse(self.raw_reply()?.as_ref())?.0)
}
pub fn reply_unchecked(self) -> Result<Option<R>, ConnectionError> {
self.raw_reply_unchecked()?
.map(|buf| R::try_parse(buf.as_ref()).map(|r| r.0))
.transpose()
.map_err(Into::into)
}
pub fn discard_reply_and_errors(self) {
let conn = self.raw_cookie.connection;
conn.discard_reply(
self.raw_cookie.into_sequence_number(),
RequestKind::HasResponse,
DiscardMode::DiscardReplyAndError,
)
}
pub(crate) fn into_sequence_number(self) -> SequenceNumber {
self.raw_cookie.into_sequence_number()
}
pub(crate) fn replace_connection<C2: RequestConnection + ?Sized>(
self,
connection: &C2,
) -> Cookie<'_, C2, R> {
Cookie {
raw_cookie: self.raw_cookie.replace_connection(connection),
phantom: PhantomData,
}
}
}
#[derive(Debug)]
pub struct CookieWithFds<'a, C, R>
where
C: RequestConnection + ?Sized,
{
raw_cookie: RawCookie<'a, C>,
phantom: PhantomData<R>,
}
impl<C, R> CookieWithFds<'_, C, R>
where
R: TryParseFd,
C: RequestConnection + ?Sized,
{
pub fn new(connection: &C, sequence_number: SequenceNumber) -> CookieWithFds<'_, C, R> {
CookieWithFds {
raw_cookie: RawCookie::new(connection, sequence_number),
phantom: PhantomData,
}
}
pub fn sequence_number(&self) -> SequenceNumber {
self.raw_cookie.sequence_number
}
pub fn raw_reply(self) -> Result<BufWithFds<C::Buf>, ReplyError> {
let conn = self.raw_cookie.connection;
conn.wait_for_reply_with_fds(self.raw_cookie.into_sequence_number())
}
pub fn reply(self) -> Result<R, ReplyError> {
let (buffer, mut fds) = self.raw_reply()?;
Ok(R::try_parse_fd(buffer.as_ref(), &mut fds)?.0)
}
pub(crate) fn replace_connection<C2: RequestConnection + ?Sized>(
self,
connection: &C2,
) -> CookieWithFds<'_, C2, R> {
CookieWithFds {
raw_cookie: self.raw_cookie.replace_connection(connection),
phantom: PhantomData,
}
}
}
macro_rules! multiple_reply_cookie {
(
$(#[$meta:meta])*
pub struct $name:ident for $reply:ident
) => {
$(#[$meta])*
#[derive(Debug)]
pub struct $name<'a, C: RequestConnection + ?Sized>(Option<RawCookie<'a, C>>);
impl<'c, C> $name<'c, C>
where
C: RequestConnection + ?Sized,
{
pub(crate) fn new(
cookie: Cookie<'c, C, $reply>,
) -> Self {
Self(Some(cookie.raw_cookie))
}
pub fn sequence_number(&self) -> Option<SequenceNumber> {
self.0.as_ref().map(|x| x.sequence_number)
}
}
impl<C> Iterator for $name<'_, C>
where
C: RequestConnection + ?Sized,
{
type Item = Result<$reply, ReplyError>;
fn next(&mut self) -> Option<Self::Item> {
let cookie = self.0.take()?;
let reply = cookie
.connection
.wait_for_reply_or_error(cookie.sequence_number);
let reply = match reply {
Err(e) => return Some(Err(e)),
Ok(v) => v,
};
let reply = $reply::try_parse(reply.as_ref());
match reply {
Ok(ref reply) if Self::is_last(&reply.0) => None,
Ok(reply) => {
self.0 = Some(cookie);
Some(Ok(reply.0))
}
Err(e) => Some(Err(e.into())),
}
}
}
}
}
multiple_reply_cookie!(
pub struct ListFontsWithInfoCookie for ListFontsWithInfoReply
);
impl<C> ListFontsWithInfoCookie<'_, C>
where
C: RequestConnection + ?Sized,
{
fn is_last(reply: &ListFontsWithInfoReply) -> bool {
reply.name.is_empty()
}
}
#[cfg(feature = "record")]
multiple_reply_cookie!(
pub struct RecordEnableContextCookie for EnableContextReply
);
#[cfg(feature = "record")]
impl<C> RecordEnableContextCookie<'_, C>
where
C: RequestConnection + ?Sized,
{
fn is_last(reply: &EnableContextReply) -> bool {
reply.category == 5
}
}