1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
//! Client library for the Mercurial command server //! //! This crate provides a client interface to the Mercurial distributed //! version control system (DVCS) in Rust, using Mercurial's [command //! server][]. The command server is designed to allow tools to be built //! around Mercurial repositories, without being tied into Mercurial's //! internal API or licensing. //! //! [command server]: https://mercurial.selenic.com/wiki/CommandServer //! //! ## High-level API //! //! The [`cmdserver`](cmdserver/index.html) module provides a high-level //! interface which manages spawning and communicating with a command //! server instance: //! //! ```rust //! use hglib::cmdserver::CommandServer; //! let cmdserver = CommandServer::new().ok().expect("failed to start command server"); //! ``` //! //! This high-level interface is largely unimplemented so far, but //! builds on the raw API that is already functional. //! //! ## Raw API //! //! The lower-level API in the [`connection`](connection/index.html) //! module allows you to run commands at the level of the command server //! protocol. Assembling the command and reading the result //! chunk-by-chunk is done manually. //! //! ```rust //! # use std::io; //! # use std::io::prelude::*; //! use hglib::connection::Connection; //! use hglib::Chunk; //! let mut conn = Connection::new().ok().expect("failed to start command server"); //! let (capabilities, encoding) = //! conn.read_hello().ok().expect("failed to read server hello"); //! //! let cmditer = //! conn.raw_command(vec![b"log", b"-l", b"5"]) //! .ok().expect("failed to send raw command"); //! for chunk in cmditer { //! match chunk { //! Ok(Chunk::Output(s)) => { io::stdout().write(&s); }, //! Ok(Chunk::Error(s)) => { io::stdout().write(&s); }, //! Ok(Chunk::Result(r)) => println!("command exited with status: {}", r), //! Ok(_) => {}, //! Err(e) => panic!("failed to read chunk: {}", e), //! } //! } //! ``` extern crate byteorder; /// A type representing a "chunk" of data received from the command server. #[derive(Debug)] pub enum Chunk { /// Data received on the output channel (equivalent to stdout). Output(Vec<u8>), /// Data received on the error channel (equivalent to stderr). Error(Vec<u8>), /// Data received on the debug channel (log entries). Debug(Vec<u8>), /// The exit code of a Mercurial command. Result(i32), /// Indicates that the client should send input of the given maximum length. Input(i32), /// Indicates that the client should send line-oriented input of the /// given maximum length. LineInput(i32), } pub mod connection; pub mod cmdserver; #[cfg(test)] mod tests { use super::cmdserver::*; use super::Chunk; #[test] fn capabilities() { let cmdserver = CommandServer::new().unwrap(); assert!(cmdserver.capabilities.len() > 0); assert!(cmdserver.capabilities.iter().any(|cap| cap == "runcommand")); } #[test] fn run_command() { let mut cmdserver = CommandServer::new().unwrap(); let run = cmdserver.connection.raw_command(vec![b"summary"]).unwrap(); let (mut result, mut output) = (-1i32, vec![]); for chunk in run { match chunk { Ok(Chunk::Output(s)) => output.extend(s), Ok(Chunk::Error(_)) => continue, Ok(Chunk::Result(r)) => result = r, Ok(c) => panic!("unexpected chunk: {:?}", c), Err(e) => panic!("failed to read command results: {}", e), } } assert!(output.starts_with(b"parent:")); assert_eq!(result, 0); } #[test] fn run_command_error() { let mut cmdserver = CommandServer::new().unwrap(); let run = cmdserver.connection.raw_command(vec![b"noexist"]).unwrap(); let (mut result, mut error) = (-1i32, vec![]); for chunk in run { match chunk { Ok(Chunk::Output(_)) => continue, Ok(Chunk::Error(s)) => error.extend(s), Ok(Chunk::Result(r)) => { result = r }, Ok(c) => panic!("unexpected chunk: {:?}", c), Err(e) => panic!("failed to read command results: {}", e), } } assert_eq!(result, 255); assert!(error.starts_with(b"hg: unknown command 'noexist'")); } }