In short, MessageLoop<Connection> was redesigned as Protocol<Connection>,
and the protocol methods no longer consume self.
API changes are briefly documented in the following page:
https://docs.rs/tokio-hglib/0.3.0/tokio_hglib/struct.Protocol.html
Alphare |
hg-reviewers |
In short, MessageLoop<Connection> was redesigned as Protocol<Connection>,
and the protocol methods no longer consume self.
API changes are briefly documented in the following page:
https://docs.rs/tokio-hglib/0.3.0/tokio_hglib/struct.Protocol.html
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
// Copyright 2018 Yuya Nishihara <yuya@tcha.org> | // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | ||||
// | // | ||||
// This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
//! Functions to send client-side fds over the command server channel. | //! Functions to send client-side fds over the command server channel. | ||||
use futures::{try_ready, Async, Future, Poll}; | |||||
use std::io; | use std::io; | ||||
use std::os::unix::io::AsRawFd; | use std::os::unix::io::AsRawFd; | ||||
use tokio_hglib::codec::ChannelMessage; | use tokio_hglib::codec::ChannelMessage; | ||||
use tokio_hglib::protocol::MessageLoop; | use tokio_hglib::{Connection, Protocol}; | ||||
use tokio_hglib::{Client, Connection}; | |||||
use crate::message; | use crate::message; | ||||
use crate::procutil; | use crate::procutil; | ||||
/// Future to send client-side fds over the command server channel. | /// Sends client-side fds over the command server channel. | ||||
/// | /// | ||||
/// This works as follows: | /// This works as follows: | ||||
/// 1. Client sends "attachio" request. | /// 1. Client sends "attachio" request. | ||||
/// 2. Server sends back 1-byte input request. | /// 2. Server sends back 1-byte input request. | ||||
/// 3. Client sends fds with 1-byte dummy payload in response. | /// 3. Client sends fds with 1-byte dummy payload in response. | ||||
/// 4. Server returns the number of the fds received. | /// 4. Server returns the number of the fds received. | ||||
/// | /// | ||||
/// If the stderr is omitted, it will be redirected to the stdout. This | /// If the stderr is omitted, it will be redirected to the stdout. This | ||||
/// allows us to attach the pager stdin to both stdout and stderr, and | /// allows us to attach the pager stdin to both stdout and stderr, and | ||||
/// dispose of the client-side handle once attached. | /// dispose of the client-side handle once attached. | ||||
#[must_use = "futures do nothing unless polled"] | pub async fn attach_io( | ||||
pub struct AttachIo<C, I, O, E> | proto: &mut Protocol<impl Connection + AsRawFd>, | ||||
where | stdin: impl AsRawFd, | ||||
C: Connection, | stdout: impl AsRawFd, | ||||
stderr: Option<impl AsRawFd>, | |||||
) -> io::Result<()> { | |||||
// TODO: unindent | |||||
{ | { | ||||
msg_loop: MessageLoop<C>, | proto.send_command("attachio").await?; | ||||
stdin: I, | |||||
stdout: O, | |||||
stderr: Option<E>, | |||||
} | |||||
impl<C, I, O, E> AttachIo<C, I, O, E> | |||||
where | |||||
C: Connection + AsRawFd, | |||||
I: AsRawFd, | |||||
O: AsRawFd, | |||||
E: AsRawFd, | |||||
{ | |||||
pub fn with_client( | |||||
client: Client<C>, | |||||
stdin: I, | |||||
stdout: O, | |||||
stderr: Option<E>, | |||||
) -> AttachIo<C, I, O, E> { | |||||
let msg_loop = MessageLoop::start(client, b"attachio"); | |||||
AttachIo { | |||||
msg_loop, | |||||
stdin, | |||||
stdout, | |||||
stderr, | |||||
} | |||||
} | |||||
} | |||||
impl<C, I, O, E> Future for AttachIo<C, I, O, E> | |||||
where | |||||
C: Connection + AsRawFd, | |||||
I: AsRawFd, | |||||
O: AsRawFd, | |||||
E: AsRawFd, | |||||
{ | |||||
type Item = Client<C>; | |||||
type Error = io::Error; | |||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | |||||
loop { | loop { | ||||
let (client, msg) = try_ready!(self.msg_loop.poll()); | match proto.fetch_response().await? { | ||||
match msg { | |||||
ChannelMessage::Data(b'r', data) => { | ChannelMessage::Data(b'r', data) => { | ||||
let fd_cnt = message::parse_result_code(data)?; | let fd_cnt = message::parse_result_code(data)?; | ||||
if fd_cnt == 3 { | if fd_cnt == 3 { | ||||
return Ok(Async::Ready(client)); | return Ok(()); | ||||
} else { | } else { | ||||
return Err(io::Error::new( | return Err(io::Error::new( | ||||
io::ErrorKind::InvalidData, | io::ErrorKind::InvalidData, | ||||
"unexpected attachio result", | "unexpected attachio result", | ||||
)); | )); | ||||
} | } | ||||
} | } | ||||
ChannelMessage::Data(..) => { | ChannelMessage::Data(..) => { | ||||
// just ignore data sent to uninteresting (optional) channel | // just ignore data sent to uninteresting (optional) channel | ||||
self.msg_loop = MessageLoop::resume(client); | |||||
} | } | ||||
ChannelMessage::InputRequest(1) => { | ChannelMessage::InputRequest(1) => { | ||||
// this may fail with EWOULDBLOCK in theory, but the | // this may fail with EWOULDBLOCK in theory, but the | ||||
// payload is quite small, and the send buffer should | // payload is quite small, and the send buffer should | ||||
// be empty so the operation will complete immediately | // be empty so the operation will complete immediately | ||||
let sock_fd = client.as_raw_fd(); | let sock_fd = proto.as_raw_fd(); | ||||
let ifd = self.stdin.as_raw_fd(); | let ifd = stdin.as_raw_fd(); | ||||
let ofd = self.stdout.as_raw_fd(); | let ofd = stdout.as_raw_fd(); | ||||
let efd = self.stderr.as_ref().map_or(ofd, |f| f.as_raw_fd()); | let efd = stderr.as_ref().map_or(ofd, |f| f.as_raw_fd()); | ||||
procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?; | procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?; | ||||
self.msg_loop = MessageLoop::resume(client); | |||||
} | } | ||||
ChannelMessage::InputRequest(..) | ChannelMessage::InputRequest(..) | ||||
| ChannelMessage::LineRequest(..) | | ChannelMessage::LineRequest(..) | ||||
| ChannelMessage::SystemRequest(..) => { | | ChannelMessage::SystemRequest(..) => { | ||||
return Err(io::Error::new( | return Err(io::Error::new( | ||||
io::ErrorKind::InvalidData, | io::ErrorKind::InvalidData, | ||||
"unsupported request while attaching io", | "unsupported request while attaching io", | ||||
)); | )); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } |
// Copyright 2018 Yuya Nishihara <yuya@tcha.org> | // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | ||||
// | // | ||||
// This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
//mod attachio; | mod attachio; | ||||
//mod clientext; | //mod clientext; | ||||
//pub mod locator; | //pub mod locator; | ||||
pub mod message; | pub mod message; | ||||
pub mod procutil; | pub mod procutil; | ||||
//mod runcommand; | //mod runcommand; | ||||
//mod uihandler; | //mod uihandler; | ||||
//pub use clientext::ChgClientExt; | //pub use clientext::ChgClientExt; | ||||
//pub use uihandler::{ChgUiHandler, SystemHandler}; | //pub use uihandler::{ChgUiHandler, SystemHandler}; |