##// END OF EJS Templates
rust-index: expose a method to retrieve the C index...
rust-index: expose a method to retrieve the C index The code for `shortesthexnodeidprefix` need to access the actual C index. For now we grant its wish and expose a method to do so. Once we have the nodemap in Rust, we will be able to implement the same feature from rust and we will be able to drop this method. Differential Revision: https://phab.mercurial-scm.org/D7658

File last commit:

r44270:ce088b38 default
r44464:443dc165 default
Show More
message.rs
133 lines | 3.8 KiB | application/rls-services+xml | RustLexer
// Copyright 2018 Yuya Nishihara <yuya@tcha.org>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
//! Utility for parsing and building command-server messages.
use bytes::Bytes;
use std::error;
use std::ffi::{OsStr, OsString};
use std::io;
use std::os::unix::ffi::OsStrExt;
pub use tokio_hglib::message::*; // re-exports
/// Shell command type requested by the server.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CommandType {
/// Pager should be spawned.
Pager,
/// Shell command should be executed to send back the result code.
System,
}
/// Shell command requested by the server.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommandSpec {
pub command: OsString,
pub current_dir: OsString,
pub envs: Vec<(OsString, OsString)>,
}
/// Parses "S" channel request into command type and spec.
pub fn parse_command_spec(data: Bytes) -> io::Result<(CommandType, CommandSpec)> {
let mut split = data.split(|&c| c == b'\0');
let ctype = parse_command_type(split.next().ok_or(new_parse_error("missing type"))?)?;
let command = split.next().ok_or(new_parse_error("missing command"))?;
let current_dir = split.next().ok_or(new_parse_error("missing current dir"))?;
let mut envs = Vec::new();
for l in split {
let mut s = l.splitn(2, |&c| c == b'=');
let k = s.next().unwrap();
let v = s.next().ok_or(new_parse_error("malformed env"))?;
envs.push((
OsStr::from_bytes(k).to_owned(),
OsStr::from_bytes(v).to_owned(),
));
}
let spec = CommandSpec {
command: OsStr::from_bytes(command).to_owned(),
current_dir: OsStr::from_bytes(current_dir).to_owned(),
envs: envs,
};
Ok((ctype, spec))
}
fn parse_command_type(value: &[u8]) -> io::Result<CommandType> {
match value {
b"pager" => Ok(CommandType::Pager),
b"system" => Ok(CommandType::System),
_ => Err(new_parse_error(format!(
"unknown command type: {}",
decode_latin1(value)
))),
}
}
fn decode_latin1<S>(s: S) -> String
where
S: AsRef<[u8]>,
{
s.as_ref().iter().map(|&c| c as char).collect()
}
fn new_parse_error<E>(error: E) -> io::Error
where
E: Into<Box<error::Error + Send + Sync>>,
{
io::Error::new(io::ErrorKind::InvalidData, error)
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::unix::ffi::OsStringExt;
#[test]
fn parse_command_spec_good() {
let src = [
b"pager".as_ref(),
b"less -FRX".as_ref(),
b"/tmp".as_ref(),
b"LANG=C".as_ref(),
b"HGPLAIN=".as_ref(),
]
.join(&0);
let spec = CommandSpec {
command: os_string_from(b"less -FRX"),
current_dir: os_string_from(b"/tmp"),
envs: vec![
(os_string_from(b"LANG"), os_string_from(b"C")),
(os_string_from(b"HGPLAIN"), os_string_from(b"")),
],
};
assert_eq!(
parse_command_spec(Bytes::from(src)).unwrap(),
(CommandType::Pager, spec)
);
}
#[test]
fn parse_command_spec_too_short() {
assert!(parse_command_spec(Bytes::from_static(b"")).is_err());
assert!(parse_command_spec(Bytes::from_static(b"pager")).is_err());
assert!(parse_command_spec(Bytes::from_static(b"pager\0less")).is_err());
}
#[test]
fn parse_command_spec_malformed_env() {
assert!(parse_command_spec(Bytes::from_static(b"pager\0less\0/tmp\0HOME")).is_err());
}
#[test]
fn parse_command_spec_unknown_type() {
assert!(parse_command_spec(Bytes::from_static(b"paper\0less")).is_err());
}
fn os_string_from(s: &[u8]) -> OsString {
OsString::from_vec(s.to_vec())
}
}