diff options
Diffstat (limited to 'ipc/src/assuan/mod.rs')
-rw-r--r-- | ipc/src/assuan/mod.rs | 104 |
1 files changed, 51 insertions, 53 deletions
diff --git a/ipc/src/assuan/mod.rs b/ipc/src/assuan/mod.rs index 55be0d66..4b1df128 100644 --- a/ipc/src/assuan/mod.rs +++ b/ipc/src/assuan/mod.rs @@ -41,16 +41,13 @@ lalrpop_util::lalrpop_mod!( /// command is sent lazily, i.e. it is only sent if you poll for the /// responses. /// -/// [`Connection::send`]: #method.send +/// [`Connection::send`]: Client::send() /// /// `Client` implements [`Stream`] to return all server responses /// until the first [`Response::Ok`], [`Response::Error`], or /// [`Response::Inquire`]. /// /// [`Stream`]: #impl-Stream -/// [`Response::Ok`]: enum.Response.html#variant.Ok -/// [`Response::Error`]: enum.Response.html#variant.Error -/// [`Response::Inquire`]: enum.Response.html#variant.Inquire /// /// [`Response::Ok`] and [`Response::Error`] indicate success and /// failure. [`Response::Inquire`] means that the server requires @@ -58,21 +55,39 @@ lalrpop_util::lalrpop_mod!( /// provided using [`Connection::data()`], or the operation may be /// canceled using [`Connection::cancel()`]. /// -/// [`Connection::data()`]: #method.data -/// [`Connection::cancel()`]: #method.cancel +/// [`Connection::data()`]: Client::data() +/// [`Connection::cancel()`]: Client::cancel() pub struct Client { r: BufReader<ReadHalf<IpcStream>>, // xxx: abstract over buffer: Vec<u8>, done: bool, w: WriteState, } +assert_send_and_sync!(Client); enum WriteState { Ready(WriteHalf<IpcStream>), - Sending(Pin<Box<dyn Future<Output = Result<WriteHalf<IpcStream>, anyhow::Error>>>>), + Sending(Pin<Box<dyn Future<Output = Result<WriteHalf<IpcStream>>> + + Send + Sync>>), Transitioning, Dead, } +assert_send_and_sync!(WriteState); + +/// Percent-escapes the given string. +pub fn escape<S: AsRef<str>>(s: S) -> String { + let mut r = String::with_capacity(s.as_ref().len()); + for c in s.as_ref().chars() { + match c { + '%' => r.push_str("%25"), + ' ' => r.push('+'), + n if n.is_ascii() && (n as u8) < 32 => + r.push_str(&format!("%{:02X}", n as u8)), + _ => r.push(c), + } + } + r +} impl Client { /// Connects to the server. @@ -96,14 +111,14 @@ impl Client { /// [`Connection::data()`], or the operation may be canceled using /// [`Connection::cancel()`]. /// - /// [`Response::Ok`]: ../assuan/enum.Response.html#variant.Ok - /// [`Response::Error`]: ../assuan/enum.Response.html#variant.Error - /// [`Response::Inquire`]: ../assuan/enum.Response.html#variant.Inquire - /// [`Connection::data()`]: #method.data - /// [`Connection::cancel()`]: #method.cancel + /// [`Response::Ok`]: super::assuan::Response::Ok + /// [`Response::Error`]: super::assuan::Response::Error + /// [`Response::Inquire`]: super::assuan::Response::Inquire + /// [`Connection::data()`]: Client::data() + /// [`Connection::cancel()`]: Client::cancel() /// /// Note: `command` is passed as-is. Control characters, like - /// `%`, must be %-escaped. + /// `%`, must be %-escaped using [`escape`]. pub fn send<'a, C: 'a>(&'a mut self, command: C) -> Result<()> where C: AsRef<[u8]> { @@ -138,7 +153,7 @@ impl Client { /// using this objects [`Stream`] implementation. /// /// [`Stream`]: #impl-Stream - pub fn cancel<'a>(&'a mut self) -> Result<()> { + pub fn cancel(&mut self) -> Result<()> { self.send("CAN") } @@ -154,16 +169,16 @@ impl Client { /// and `Error` indicate success and failure of the original /// operation that lead to the current inquiry. /// - /// [`Response::Ok`]: ../assuan/enum.Response.html#variant.Ok - /// [`Response::Error`]: ../assuan/enum.Response.html#variant.Error - /// [`Response::Inquire`]: ../assuan/enum.Response.html#variant.Inquire + /// [`Response::Ok`]: super::assuan::Response::Ok + /// [`Response::Error`]: super::assuan::Response::Error + /// [`Response::Inquire`]: super::assuan::Response::Inquire pub fn data<'a, C: 'a>(&'a mut self, data: C) -> Result<()> where C: AsRef<[u8]> { let mut data = data.as_ref(); let mut request = Vec::with_capacity(data.len()); while ! data.is_empty() { - if request.len() > 0 { + if !request.is_empty() { request.push(0x0a); } write!(&mut request, "D ").unwrap(); @@ -299,32 +314,24 @@ impl Stream for Client { } // No more linebreaks in the buffer. We need to get more. - // First, grow the buffer. - let buffer_len = buffer.len(); - buffer.resize(buffer_len + MAX_LINE_LENGTH, 0); - - match reader.as_mut().poll_read(cx, &mut buffer[buffer_len..])? { - Poll::Ready(n_read) if n_read == 0 => { - // EOF. - buffer.resize(buffer_len, 0); - if ! buffer.is_empty() { - // Incomplete server response. - return Poll::Ready(Some(Err(Error::ConnectionClosed( - buffer.clone()).into()))); - + // First, get a new read buffer. + // Later, append the read data to the Client's buffer + + let mut vec = vec![0u8; MAX_LINE_LENGTH]; + let mut read_buf = tokio::io::ReadBuf::new(&mut vec); + + match reader.as_mut().poll_read(cx, &mut read_buf)? { + Poll::Ready(()) => { + if read_buf.filled().is_empty() { + // End of stream. + return Poll::Ready(None) + } else { + buffer.extend_from_slice(read_buf.filled()); + continue; } - - // End of stream. - return Poll::Ready(None); - }, - - Poll::Ready(n_read) => { - buffer.resize(buffer_len + n_read, 0); - continue; }, Poll::Pending => { - buffer.resize(buffer_len, 0); return Poll::Pending; }, } @@ -409,33 +416,24 @@ impl Response { } } Err(anyhow::anyhow!( - String::from_utf8_lossy(&msg).to_string()).into()) + String::from_utf8_lossy(&msg).to_string())) }, } } /// Returns true if this message indicates success. pub fn is_ok(&self) -> bool { - match self { - Response::Ok { .. } => true, - _ => false, - } + matches!(self, Response::Ok { .. } ) } /// Returns true if this message indicates an error. pub fn is_err(&self) -> bool { - match self { - Response::Error { .. } => true, - _ => false, - } + matches!(self, Response::Error { .. }) } /// Returns true if this message is an inquiry. pub fn is_inquire(&self) -> bool { - match self { - Response::Inquire { .. } => true, - _ => false, - } + matches!(self, Response::Inquire { .. }) } /// Returns true if this response concludes the server's response. |