use std::net; use crossbeam_channel as chan; use nakamoto_net::Waker; use thiserror::Error; use crate::identity::ProjId; use crate::protocol; use crate::protocol::{CommandError, FetchLookup}; /// An error resulting from a handle method. #[derive(Error, Debug)] pub enum Error { /// The command channel is no longer connected. #[error("command channel is not connected")] NotConnected, /// The command returned an error. #[error("command failed: {0}")] Command(#[from] CommandError), /// The operation timed out. #[error("the operation timed out")] Timeout, /// An I/O error occured. #[error(transparent)] Io(#[from] std::io::Error), } impl From for Error { fn from(_: chan::RecvError) -> Self { Self::NotConnected } } impl From for Error { fn from(err: chan::RecvTimeoutError) -> Self { match err { chan::RecvTimeoutError::Timeout => Self::Timeout, chan::RecvTimeoutError::Disconnected => Self::NotConnected, } } } impl From> for Error { fn from(_: chan::SendError) -> Self { Self::NotConnected } } pub struct Handle { pub(crate) commands: chan::Sender, pub(crate) shutdown: chan::Sender<()>, pub(crate) listening: chan::Receiver, pub(crate) waker: W, } impl traits::Handle for Handle { /// Retrieve or update the given project from the network. fn fetch(&self, id: ProjId) -> Result { let (sender, receiver) = chan::bounded(1); self.commands.send(protocol::Command::Fetch(id, sender))?; receiver.recv().map_err(Error::from) } /// Start tracking the given project. Doesn't do anything if the project is already tracked. fn track(&self, id: ProjId) -> Result { let (sender, receiver) = chan::bounded(1); self.commands.send(protocol::Command::Track(id, sender))?; receiver.recv().map_err(Error::from) } /// Untrack the given project and delete it from storage. fn untrack(&self, id: ProjId) -> Result { let (sender, receiver) = chan::bounded(1); self.commands.send(protocol::Command::Untrack(id, sender))?; receiver.recv().map_err(Error::from) } /// Notify the client that a project has been updated. fn updated(&self, id: ProjId) -> Result<(), Error> { self.command(protocol::Command::AnnounceRefsUpdate(id)) } /// Send a command to the command channel, and wake up the event loop. fn command(&self, cmd: protocol::Command) -> Result<(), Error> { self.commands.send(cmd)?; self.waker.wake()?; Ok(()) } /// Ask the client to shutdown. fn shutdown(self) -> Result<(), Error> { self.shutdown.send(())?; self.waker.wake()?; Ok(()) } } pub mod traits { use super::*; pub trait Handle { /// Retrieve or update the project from network. fn fetch(&self, id: ProjId) -> Result; /// Start tracking the given project. Doesn't do anything if the project is already /// tracked. fn track(&self, id: ProjId) -> Result; /// Untrack the given project and delete it from storage. fn untrack(&self, id: ProjId) -> Result; /// Notify the client that a project has been updated. fn updated(&self, id: ProjId) -> Result<(), Error>; /// Send a command to the command channel, and wake up the event loop. fn command(&self, cmd: protocol::Command) -> Result<(), Error>; /// Ask the client to shutdown. fn shutdown(self) -> Result<(), Error>; } }