summaryrefslogtreecommitdiffstats
path: root/ipc/src/assuan/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/src/assuan/mod.rs')
-rw-r--r--ipc/src/assuan/mod.rs104
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.