]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::{ |
2 | io, | |
3 | pin::Pin, | |
4 | task::{Context, Poll}, | |
5 | }; | |
6 | ||
7 | use futures_io::AsyncWrite; | |
8 | use pin_project_lite::pin_project; | |
9 | ||
10 | use crate::client::{ExtendedBufRead, MessageKind, WriteMode}; | |
11 | ||
12 | pin_project! { | |
13 | /// A [`Write`][io::Write] implementation optimized for writing packet lines. | |
14 | /// A type implementing `Write` for packet lines, which when done can be transformed into a `Read` for | |
15 | /// obtaining the response. | |
16 | pub struct RequestWriter<'a> { | |
17 | on_into_read: MessageKind, | |
18 | #[pin] | |
19 | writer: gix_packetline::Writer<Box<dyn AsyncWrite + Unpin + 'a>>, | |
20 | reader: Box<dyn ExtendedBufRead + Unpin + 'a>, | |
21 | } | |
22 | } | |
23 | impl<'a> futures_io::AsyncWrite for RequestWriter<'a> { | |
24 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> { | |
25 | self.project().writer.poll_write(cx, buf) | |
26 | } | |
27 | ||
28 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | |
29 | self.project().writer.poll_flush(cx) | |
30 | } | |
31 | ||
32 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | |
33 | self.project().writer.poll_close(cx) | |
34 | } | |
35 | } | |
36 | ||
37 | /// methods with bonds to IO | |
38 | impl<'a> RequestWriter<'a> { | |
39 | /// Create a new instance from a `writer` (commonly a socket), a `reader` into which to transform once the | |
40 | /// writes are finished, along with configuration for the `write_mode` and information about which message to write | |
41 | /// when this instance is converted [into a `reader`][RequestWriter::into_read()] to read the request's response. | |
42 | pub fn new_from_bufread<W: AsyncWrite + Unpin + 'a>( | |
43 | writer: W, | |
44 | reader: Box<dyn ExtendedBufRead + Unpin + 'a>, | |
45 | write_mode: WriteMode, | |
46 | on_into_read: MessageKind, | |
47 | ) -> Self { | |
48 | let mut writer = gix_packetline::Writer::new(Box::new(writer) as Box<dyn AsyncWrite + Unpin>); | |
49 | match write_mode { | |
50 | WriteMode::Binary => writer.enable_binary_mode(), | |
51 | WriteMode::OneLfTerminatedLinePerWriteCall => writer.enable_text_mode(), | |
52 | } | |
53 | RequestWriter { | |
54 | on_into_read, | |
55 | writer, | |
56 | reader, | |
57 | } | |
58 | } | |
59 | ||
60 | /// Write the given message as packet line. | |
61 | pub async fn write_message(&mut self, message: MessageKind) -> io::Result<()> { | |
62 | match message { | |
63 | MessageKind::Flush => { | |
64 | gix_packetline::PacketLineRef::Flush | |
65 | .write_to(self.writer.inner_mut()) | |
66 | .await | |
67 | } | |
68 | MessageKind::Delimiter => { | |
69 | gix_packetline::PacketLineRef::Delimiter | |
70 | .write_to(self.writer.inner_mut()) | |
71 | .await | |
72 | } | |
73 | MessageKind::ResponseEnd => { | |
74 | gix_packetline::PacketLineRef::ResponseEnd | |
75 | .write_to(self.writer.inner_mut()) | |
76 | .await | |
77 | } | |
78 | MessageKind::Text(t) => gix_packetline::TextRef::from(t).write_to(self.writer.inner_mut()).await, | |
79 | } | |
80 | .map(|_| ()) | |
81 | } | |
82 | /// Discard the ability to write and turn this instance into the reader for obtaining the other side's response. | |
83 | /// | |
84 | /// Doing so will also write the message type this instance was initialized with. | |
85 | pub async fn into_read(mut self) -> std::io::Result<Box<dyn ExtendedBufRead + Unpin + 'a>> { | |
86 | self.write_message(self.on_into_read).await?; | |
87 | Ok(self.reader) | |
88 | } | |
89 | ||
fe692bf9 | 90 | /// Dissolve this instance into its write and read handles without any message-writing side-effect as in [`RequestWriter::into_read()`]. |
0a29b90c FG |
91 | /// |
92 | /// Furthermore, the writer will not encode everything it writes as packetlines, but write everything verbatim into the | |
93 | /// underlying channel. | |
94 | /// | |
95 | /// # Note | |
96 | /// | |
97 | /// It's of utmost importance to drop the request writer before reading the response as these might be inter-dependent, depending on | |
98 | /// the underlying transport mechanism. Failure to do so may result in a deadlock depending on how the write and read mechanism | |
99 | /// is implemented. | |
100 | pub fn into_parts(self) -> (Box<dyn AsyncWrite + Unpin + 'a>, Box<dyn ExtendedBufRead + Unpin + 'a>) { | |
101 | (self.writer.into_inner(), self.reader) | |
102 | } | |
103 | } |