Patchwork [4,of,8] rust-hglib: extract object managing command-server process

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

Comments

Yuya Nishihara - April 1, 2018, 11:14 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1522481035 -32400
#      Sat Mar 31 16:23:55 2018 +0900
# Node ID a3c01fe6cf0a036859f76d8c222c4ca9aa32116c
# Parent  0502f8498e8da8f0608181619d49d4a8f7ce9637
rust-hglib: extract object managing command-server process

A stub for supporting domain sockets. We could design it as a Read/Write
wrapper like UnixStream, but that would make borrowing thingy difficult
when we want to wrap IOs by BufReader/Writer.

Patch

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
@@ -22,7 +22,7 @@  use std::error::Error;
 use std::fmt::{self, Display};
 use std::io;
 use std::io::prelude::*;
-use std::process::{Command, Stdio, Child, ChildStdout, ExitStatus};
+use std::process::{Command, Stdio, Child, ChildStdin, ChildStdout, ExitStatus};
 use std::str;
 
 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
@@ -121,32 +121,53 @@  impl<'a> Iterator for CommandRun<'a> {
     }
 }
 
+/// A connection to new command server over stdin/stdout pipe
+#[derive(Debug)]
+pub struct PipeBackend {
+    child: Child,
+}
+
+impl PipeBackend {
+    pub fn spawn() -> io::Result<PipeBackend> {
+        let child = try!(
+            Command::new("hg")
+                .args(&["serve", "--cmdserver", "pipe", "--config", "ui.interactive=True"])
+                .stdin(Stdio::piped())
+                .stdout(Stdio::piped())
+                .spawn());
+        Ok(PipeBackend { child: child })
+    }
+
+    // We just unwrap the Option<ChildStd*> because we know that
+    // we set up the pipe in Connection::new(). We have to call
+    // .as_mut() because .unwrap()'s signature moves the `self`
+    // value out.
+
+    fn get_reader(&mut self) -> &mut ChildStdout {
+        self.child.stdout.as_mut().unwrap()
+    }
+
+    fn get_writer(&mut self) -> &mut ChildStdin {
+        self.child.stdin.as_mut().unwrap()
+    }
+
+    fn close(&mut self) -> io::Result<ExitStatus> {
+        // This will close the command server's stdin, which signals
+        // that it should exit. Returns the command server's exit code.
+        self.child.wait()
+    }
+}
+
 /// A handle to a running command server instance.
 pub struct Connection {
-    child: Child,
+    backend: PipeBackend,
 }
 
 impl Connection {
     /// Spawns a new command server process.
     pub fn new() -> io::Result<Connection> {
-        let cmdserver = try!(
-            Command::new("hg")
-                .args(&["serve", "--cmdserver", "pipe", "--config", "ui.interactive=True"])
-                .stdin(Stdio::piped())
-                .stdout(Stdio::piped())
-                .spawn());
-
-        Ok(Connection {
-            child: cmdserver,
-        })
-    }
-
-    fn child_stdout(&mut self) -> &mut ChildStdout {
-        // We just unwrap the Option<ChildStdout> because we know that
-        // we set up the pipe in Connection::new(). We have to call
-        // .as_mut() because .unwrap()'s signature moves the `self`
-        // value out.
-        self.child.stdout.as_mut().unwrap()
+        let backend = try!(PipeBackend::spawn());
+        Ok(Connection { backend: backend })
     }
 
     /// Reads and parses the server hello message. Returns a tuple of
@@ -207,7 +228,7 @@  impl Connection {
     }
 
     fn read_header(&mut self) -> io::Result<(Channel, i32)> {
-        let pout = self.child_stdout();
+        let pout = self.backend.get_reader();
         let chan = try!(pout.read_u8());
         let chan = try!(Channel::from_u8(chan));
         let length = try!(pout.read_i32::<BigEndian>());
@@ -215,14 +236,14 @@  impl Connection {
     }
 
     fn read_body(&mut self, length: i32) -> io::Result<Vec<u8>> {
-        let pout = self.child_stdout();
+        let pout = self.backend.get_reader();
         let mut buf = Vec::with_capacity(length as usize);
         try!(pout.take(length as u64).read_to_end(&mut buf));
         Ok(buf)
     }
 
     fn read_result(&mut self) -> io::Result<i32> {
-        let pout = self.child_stdout();
+        let pout = self.backend.get_reader();
         let result = try!(pout.read_i32::<BigEndian>());
         Ok(result)
     }
@@ -235,7 +256,7 @@  impl Connection {
             return Err(io::Error::new(io::ErrorKind::InvalidInput, "message too long"));
         }
 
-        let pin = self.child.stdin.as_mut().unwrap();
+        let pin = self.backend.get_writer();
         try!(pin.write(b"runcommand\n"));
         try!(pin.write_i32::<BigEndian>(len as i32));
         try!(pin.write(command[0]));
@@ -258,8 +279,6 @@  impl Connection {
 
     /// Shuts down the command server process.
     pub fn close(&mut self) -> io::Result<ExitStatus> {
-        // This will close the command server's stdin, which signals
-        // that it should exit. Returns the command server's exit code.
-        self.child.wait()
+        self.backend.close()
     }
 }