##// END OF EJS Templates
rawdata: update callers in debugcommands...
rawdata: update callers in debugcommands We update callers incrementally because this help bisecting failures. This was useful during development, so we expect it might be useful again in the future.

File last commit:

r40007:b1d8acd8 default
r43018:a6808d41 default
Show More
message.rs
117 lines | 3.7 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 std::os::unix::ffi::OsStringExt;
use super::*;
#[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())
}
}