lapin 1.0.0-alpha1

AMQP client library
Documentation
use crate::{
    channels::Channels, promises::Promises, types::ShortUInt, waker::Waker, Error, Result,
};
use crossbeam_channel::{Receiver, Sender};
use log::trace;

pub(crate) struct InternalRPC {
    rpc_in: Sender<InternalCommand>,
    rpc_out: Receiver<InternalCommand>,
    waker: Waker,
    internal_promises: Promises<()>,
}

#[derive(Clone)]
pub(crate) struct InternalRPCHandle {
    sender: Sender<InternalCommand>,
    waker: Waker,
}

impl InternalRPCHandle {
    pub(crate) fn close_connection(
        &self,
        reply_code: ShortUInt,
        reply_text: String,
        class_id: ShortUInt,
        method_id: ShortUInt,
    ) -> Result<()> {
        self.send(InternalCommand::CloseConnection(
            reply_code, reply_text, class_id, method_id,
        ))
    }

    pub(crate) fn send_connection_close_ok(&self, error: Error) -> Result<()> {
        self.send(InternalCommand::SendConnectionCloseOk(error))
    }

    pub(crate) fn remove_channel(&self, channel_id: u16, error: Error) -> Result<()> {
        self.send(InternalCommand::RemoveChannel(channel_id, error))
    }

    pub(crate) fn set_connection_closing(&self) -> Result<()> {
        self.send(InternalCommand::SetConnectionClosing)
    }

    pub(crate) fn set_connection_closed(&self, error: Error) -> Result<()> {
        self.send(InternalCommand::SetConnectionClosed(error))
    }

    pub(crate) fn set_connection_error(&self, error: Error) -> Result<()> {
        self.send(InternalCommand::SetConnectionError(error))
    }

    fn send(&self, command: InternalCommand) -> Result<()> {
        trace!("Queuing internal RPC command: {:?}", command);
        self.sender.send(command).expect("internal RPC failed");
        self.waker.wake()
    }
}

#[derive(Debug)]
enum InternalCommand {
    CloseConnection(ShortUInt, String, ShortUInt, ShortUInt),
    SendConnectionCloseOk(Error),
    RemoveChannel(u16, Error),
    SetConnectionClosing,
    SetConnectionClosed(Error),
    SetConnectionError(Error),
}

impl InternalRPC {
    pub(crate) fn new(waker: Waker, internal_promises: Promises<()>) -> Self {
        let (rpc_in, rpc_out) = crossbeam_channel::unbounded();
        Self {
            rpc_in,
            rpc_out,
            waker,
            internal_promises,
        }
    }

    pub(crate) fn handle(&self) -> InternalRPCHandle {
        InternalRPCHandle {
            sender: self.rpc_in.clone(),
            waker: self.waker.clone(),
        }
    }

    pub(crate) fn poll(&self, channels: &Channels) -> Result<()> {
        while let Ok(command) = self.rpc_out.try_recv() {
            self.run(command, channels)?;
        }
        self.poll_internal_promises(channels)
    }

    fn run(&self, command: InternalCommand, channels: &Channels) -> Result<()> {
        use InternalCommand::*;

        trace!("Handling internal RPC command: {:?}", command);
        match command {
            CloseConnection(reply_code, reply_text, class_id, method_id) => channels
                .get(0)
                .and_then(|channel0| {
                    self.internal_promises.register(channel0.connection_close(
                        reply_code,
                        &reply_text,
                        class_id,
                        method_id,
                    ))
                })
                .unwrap_or(Ok(())),
            SendConnectionCloseOk(error) => channels
                .get(0)
                .and_then(|channel| {
                    self.internal_promises
                        .register(channel.connection_close_ok(error))
                })
                .unwrap_or(Ok(())),
            RemoveChannel(channel_id, error) => channels.remove(channel_id, error),
            SetConnectionClosing => {
                channels.set_connection_closing();
                Ok(())
            }
            SetConnectionClosed(error) => channels.set_connection_closed(error),
            SetConnectionError(error) => channels.set_connection_error(error),
        }
    }

    fn poll_internal_promises(&self, channels: &Channels) -> Result<()> {
        if let Some(results) = self.internal_promises.try_wait() {
            for res in results {
                if let Err(err) = res {
                    channels.set_connection_error(err)?;
                }
            }
        }
        Ok(())
    }
}