Patchwork [8,of,8] rust-hglib: add connection backend for UNIX server

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

Comments

Yuya Nishihara - April 1, 2018, 11:14 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1522483627 -32400
#      Sat Mar 31 17:07:07 2018 +0900
# Node ID eb51fa65d0368942e578cc918ee664b7d99754e6
# Parent  e6389a767f7198a743558d4b60fb13d6f33dd376
rust-hglib: add connection backend for UNIX server

No idea if this will be useful when rewriting chg with Rust since chg does
weird fd hacks.

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
@@ -25,6 +25,13 @@  use std::io::prelude::*;
 use std::process::{Command, Stdio, Child, ChildStdin, ChildStdout};
 use std::str;
 
+#[cfg(target_family = "unix")]
+use std::net::Shutdown;
+#[cfg(target_family = "unix")]
+use std::os::unix::net::UnixStream;
+#[cfg(target_family = "unix")]
+use std::path::Path;
+
 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
 
 #[derive(Debug)]
@@ -180,6 +187,39 @@  impl ConnectionBackend for PipeBackend {
     }
 }
 
+/// A connection to running command server over UNIX domain socket
+#[cfg(target_family = "unix")]
+#[derive(Debug)]
+pub struct UnixStreamBackend {
+    stream: UnixStream
+}
+
+#[cfg(target_family = "unix")]
+impl UnixStreamBackend {
+    pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStreamBackend> {
+        let stream = try!(UnixStream::connect(path));
+        Ok(UnixStreamBackend { stream: stream })
+    }
+}
+
+#[cfg(target_family = "unix")]
+impl ConnectionBackend for UnixStreamBackend {
+    type R = UnixStream;
+    type W = UnixStream;
+
+    fn get_reader(&mut self) -> &mut UnixStream {
+        &mut self.stream
+    }
+
+    fn get_writer(&mut self) -> &mut UnixStream {
+        &mut self.stream
+    }
+
+    fn close(&mut self) -> io::Result<()> {
+        self.stream.shutdown(Shutdown::Write)
+    }
+}
+
 /// A handle to a running command server instance.
 pub struct Connection<B> {
     backend: B,
@@ -193,6 +233,15 @@  impl Connection<PipeBackend> {
     }
 }
 
+#[cfg(target_family = "unix")]
+impl Connection<UnixStreamBackend> {
+    /// Connects to a running command server over UNIX domain socket
+    pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<Connection<UnixStreamBackend>> {
+        let backend = try!(UnixStreamBackend::connect(path));
+        Ok(Connection { backend: backend })
+    }
+}
+
 impl<B> Connection<B> where B: ConnectionBackend {
     /// Reads and parses the server hello message. Returns a tuple of
     /// ([capabilities], encoding).
diff --git a/rust/hglib/tests/connection.rs b/rust/hglib/tests/connection.rs
--- a/rust/hglib/tests/connection.rs
+++ b/rust/hglib/tests/connection.rs
@@ -96,3 +96,54 @@  fn raw_command_error() {
         connection.close().unwrap();
     });
 }
+
+#[cfg(target_family = "unix")]
+mod unix {
+    use super::*;
+    use std::fs::File;
+    use std::path::Path;
+    use std::thread;
+    use std::time::Duration;
+
+    fn with_temp_unix_server<F>(f: F) where F: Fn(&str) {
+        let lock_path = ".hg/server.lock";
+        let socket_path = ".hg/server";
+        with_temp_repo(|| {
+            File::create(lock_path).unwrap();
+            let mut server = Command::new("hg")
+                .args(&["serve", "--cmdserver", "unix", "-q", "--config", "ui.interactive=True"])
+                .arg("--daemon-postexec").arg(&format!("unlink:{}", &lock_path))
+                .arg("-a").arg(&socket_path)
+                .spawn().unwrap();
+            while Path::new(lock_path).exists() {
+                thread::sleep(Duration::from_secs(1));
+                match server.try_wait().unwrap() {
+                    Some(_) => panic!("server failed to start"),
+                    None => {},
+                }
+            }
+
+            f(&socket_path);
+
+            server.kill().unwrap();
+        });
+    }
+
+    #[test]
+        fn raw_command() {
+        with_temp_unix_server(|path| {
+            let mut connection = Connection::connect(path).unwrap();
+            raw_command_with(&mut connection);
+            connection.close().unwrap();
+        });
+    }
+
+    #[test]
+    fn raw_command_error() {
+        with_temp_unix_server(|path| {
+            let mut connection = Connection::connect(path).unwrap();
+            raw_command_error_with(&mut connection);
+            connection.close().unwrap();
+        });
+    }
+}