Patchwork [6,of,8] rust-hglib: add abstraction for connection backend

login
register
mail settings
Submitter Yuya Nishihara
Date April 1, 2018, 11:14 a.m.
Message ID <8b115cac0dbdb6c140a8.1522581262@mimosa>
Download mbox | patch
Permalink /patch/30121/
State Accepted
Headers show

Comments

Yuya Nishihara - April 1, 2018, 11:14 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1522482522 -32400
#      Sat Mar 31 16:48:42 2018 +0900
# Node ID 8b115cac0dbdb6c140a838e9e986c153eb0aa3a0
# Parent  6edafeebb3c1440a23bb84f24efa0ff24161890d
rust-hglib: add abstraction for connection backend

The backends are switched statically. It could be dynamic dispatch, but I'm
not sure which will be better. The type signature appears not scary than
I thought, so let's start with the static implementation.

"impl Connection<PipeBackend>" might not be a good way to define a
"constructor."

Patch

diff --git a/rust/hglib/examples/client.rs b/rust/hglib/examples/client.rs
--- a/rust/hglib/examples/client.rs
+++ b/rust/hglib/examples/client.rs
@@ -2,11 +2,13 @@  extern crate hglib;
 extern crate tempdir;
 
 use hglib::cmdserver::CommandServer;
+use hglib::connection::ConnectionBackend;
 use hglib::Chunk;
 use std::env;
 use tempdir::TempDir;
 
-fn run_command(cmdserver: &mut CommandServer, command: Vec<&[u8]>) -> (i32, Vec<u8>, Vec<u8>) {
+fn run_command<B>(cmdserver: &mut CommandServer<B>, command: Vec<&[u8]>)
+                  -> (i32, Vec<u8>, Vec<u8>) where B: ConnectionBackend {
     let (mut result, mut output, mut error) =
         (-1i32, vec![], vec![]);
     let run = cmdserver.connection
diff --git a/rust/hglib/src/cmdserver.rs b/rust/hglib/src/cmdserver.rs
--- a/rust/hglib/src/cmdserver.rs
+++ b/rust/hglib/src/cmdserver.rs
@@ -20,18 +20,18 @@  use std::io;
 use connection::*;
 
 /// Spawns and communicates with a command server process.
-pub struct CommandServer {
+pub struct CommandServer<B> {
     /// A handle on the spawned process.
-    pub connection: Connection,
+    pub connection: Connection<B>,
     /// The list of capabilities the server reported on startup.
     pub capabilities: Vec<String>,
     /// The character encoding the server reported on startup.
     pub encoding: String,
 }
 
-impl CommandServer {
+impl CommandServer<PipeBackend> {
     /// Constructs and starts up a command server instance, or returns an error.
-    pub fn new() -> io::Result<CommandServer> {
+    pub fn new() -> io::Result<CommandServer<PipeBackend>> {
         let mut conn = try!(Connection::new());
         let (capabilities, encoding) = match conn.read_hello() {
             Ok((caps, enc)) => (caps, enc),
diff --git a/rust/hglib/src/connection.rs b/rust/hglib/src/connection.rs
--- a/rust/hglib/src/connection.rs
+++ b/rust/hglib/src/connection.rs
@@ -75,18 +75,28 @@  impl Channel {
     }
 }
 
+/// A connection to command server.
+pub trait ConnectionBackend {
+    type R: io::Read;
+    type W: io::Write;
+
+    fn get_reader(&mut self) -> &mut Self::R;
+    fn get_writer(&mut self) -> &mut Self::W;
+    fn close(&mut self) -> io::Result<()>;
+}
+
 use Chunk;
 
 /// An iterator over the results of a single Mercurial command.
 ///
 /// Each iteration yields a [`Chunk`](../enum.Chunk.html) with the
 /// output data or length of input the server is expecting.
-pub struct CommandRun<'a> {
-    connection: &'a mut Connection,
+pub struct CommandRun<'a, B: 'a> {
+    connection: &'a mut Connection<B>,
     done: bool,
 }
 
-impl<'a> Iterator for CommandRun<'a> {
+impl<'a, B> Iterator for CommandRun<'a, B> where B: ConnectionBackend {
     type Item = io::Result<Chunk>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -137,6 +147,11 @@  impl PipeBackend {
                 .spawn());
         Ok(PipeBackend { child: child })
     }
+}
+
+impl ConnectionBackend for PipeBackend {
+    type R = ChildStdout;
+    type W = ChildStdin;
 
     // We just unwrap the Option<ChildStd*> because we know that
     // we set up the pipe in Connection::new(). We have to call
@@ -166,17 +181,19 @@  impl PipeBackend {
 }
 
 /// A handle to a running command server instance.
-pub struct Connection {
-    backend: PipeBackend,
+pub struct Connection<B> {
+    backend: B,
 }
 
-impl Connection {
+impl Connection<PipeBackend> {
     /// Spawns a new command server process.
-    pub fn new() -> io::Result<Connection> {
+    pub fn new() -> io::Result<Connection<PipeBackend>> {
         let backend = try!(PipeBackend::spawn());
         Ok(Connection { backend: backend })
     }
+}
 
+impl<B> Connection<B> where B: ConnectionBackend {
     /// Reads and parses the server hello message. Returns a tuple of
     /// ([capabilities], encoding).
     ///
@@ -276,7 +293,7 @@  impl Connection {
 
     /// Sends the given `command` to Mercurial, returning an iterator
     /// over the results.
-    pub fn raw_command(&mut self, command: Vec<&[u8]>) -> io::Result<CommandRun> {
+    pub fn raw_command(&mut self, command: Vec<&[u8]>) -> io::Result<CommandRun<B>> {
         try!(self._raw_command(command));
         Ok(CommandRun {
             connection: self,