##// END OF EJS Templates
rust-chg: add parser for request messages sent to "S" channel...
Yuya Nishihara -
r40007:b1d8acd8 default
parent child Browse files
Show More
@@ -0,0 +1,117 b''
1 // Copyright 2018 Yuya Nishihara <yuya@tcha.org>
2 //
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
5
6 //! Utility for parsing and building command-server messages.
7
8 use bytes::Bytes;
9 use std::error;
10 use std::ffi::{OsStr, OsString};
11 use std::io;
12 use std::os::unix::ffi::OsStrExt;
13
14 pub use tokio_hglib::message::*; // re-exports
15
16 /// Shell command type requested by the server.
17 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
18 pub enum CommandType {
19 /// Pager should be spawned.
20 Pager,
21 /// Shell command should be executed to send back the result code.
22 System,
23 }
24
25 /// Shell command requested by the server.
26 #[derive(Clone, Debug, Eq, PartialEq)]
27 pub struct CommandSpec {
28 pub command: OsString,
29 pub current_dir: OsString,
30 pub envs: Vec<(OsString, OsString)>,
31 }
32
33 /// Parses "S" channel request into command type and spec.
34 pub fn parse_command_spec(data: Bytes) -> io::Result<(CommandType, CommandSpec)> {
35 let mut split = data.split(|&c| c == b'\0');
36 let ctype = parse_command_type(split.next().ok_or(new_parse_error("missing type"))?)?;
37 let command = split.next().ok_or(new_parse_error("missing command"))?;
38 let current_dir = split.next().ok_or(new_parse_error("missing current dir"))?;
39
40 let mut envs = Vec::new();
41 for l in split {
42 let mut s = l.splitn(2, |&c| c == b'=');
43 let k = s.next().unwrap();
44 let v = s.next().ok_or(new_parse_error("malformed env"))?;
45 envs.push((OsStr::from_bytes(k).to_owned(), OsStr::from_bytes(v).to_owned()));
46 }
47
48 let spec = CommandSpec {
49 command: OsStr::from_bytes(command).to_owned(),
50 current_dir: OsStr::from_bytes(current_dir).to_owned(),
51 envs: envs,
52 };
53 Ok((ctype, spec))
54 }
55
56 fn parse_command_type(value: &[u8]) -> io::Result<CommandType> {
57 match value {
58 b"pager" => Ok(CommandType::Pager),
59 b"system" => Ok(CommandType::System),
60 _ => Err(new_parse_error(format!("unknown command type: {}", decode_latin1(value)))),
61 }
62 }
63
64 fn decode_latin1<S>(s: S) -> String
65 where S: AsRef<[u8]>,
66 {
67 s.as_ref().iter().map(|&c| c as char).collect()
68 }
69
70 fn new_parse_error<E>(error: E) -> io::Error
71 where E: Into<Box<error::Error + Send + Sync>>,
72 {
73 io::Error::new(io::ErrorKind::InvalidData, error)
74 }
75
76 #[cfg(test)]
77 mod tests {
78 use std::os::unix::ffi::OsStringExt;
79 use super::*;
80
81 #[test]
82 fn parse_command_spec_good() {
83 let src = [b"pager".as_ref(),
84 b"less -FRX".as_ref(),
85 b"/tmp".as_ref(),
86 b"LANG=C".as_ref(),
87 b"HGPLAIN=".as_ref()].join(&0);
88 let spec = CommandSpec {
89 command: os_string_from(b"less -FRX"),
90 current_dir: os_string_from(b"/tmp"),
91 envs: vec![(os_string_from(b"LANG"), os_string_from(b"C")),
92 (os_string_from(b"HGPLAIN"), os_string_from(b""))],
93 };
94 assert_eq!(parse_command_spec(Bytes::from(src)).unwrap(), (CommandType::Pager, spec));
95 }
96
97 #[test]
98 fn parse_command_spec_too_short() {
99 assert!(parse_command_spec(Bytes::from_static(b"")).is_err());
100 assert!(parse_command_spec(Bytes::from_static(b"pager")).is_err());
101 assert!(parse_command_spec(Bytes::from_static(b"pager\0less")).is_err());
102 }
103
104 #[test]
105 fn parse_command_spec_malformed_env() {
106 assert!(parse_command_spec(Bytes::from_static(b"pager\0less\0/tmp\0HOME")).is_err());
107 }
108
109 #[test]
110 fn parse_command_spec_unknown_type() {
111 assert!(parse_command_spec(Bytes::from_static(b"paper\0less")).is_err());
112 }
113
114 fn os_string_from(s: &[u8]) -> OsString {
115 OsString::from_vec(s.to_vec())
116 }
117 }
@@ -3,6 +3,9 b''
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 extern crate bytes;
6 extern crate libc;
7 extern crate libc;
8 extern crate tokio_hglib;
7
9
10 pub mod message;
8 pub mod procutil;
11 pub mod procutil;
General Comments 0
You need to be logged in to leave comments. Login now