1extern crate grpcio_compiler;
2extern crate protobuf;
3extern crate protoc;
4extern crate protoc_rust;
5extern crate tempdir;
6
7use std::fs;
8use std::io;
9use std::io::Read;
10use std::io::Write;
11
12pub type Error = io::Error;
13pub type Result<T> = io::Result<T>;
14
15#[derive(Debug, Default)]
16pub struct Args<'a> {
17 pub out_dir: &'a str,
19 pub includes: &'a [&'a str],
21 pub input: &'a [&'a str],
23 pub rust_protobuf: bool,
25}
26
27pub fn run(args: Args) -> Result<()> {
28 let protoc = protoc::Protoc::from_env_path();
29 let version = protoc.version().expect("protoc version");
30 if !version.is_3() {
31 panic!("protobuf must have version 3");
32 }
33
34 if args.rust_protobuf {
35 protoc_rust::run(protoc_rust::Args {
36 out_dir: args.out_dir,
37 includes: args.includes,
38 input: args.input,
39 ..Default::default()
40 })?;
41 }
42
43 let temp_dir = tempdir::TempDir::new("protoc-rust")?;
44 let temp_file = temp_dir.path().join("descriptor.pbbin");
45 let temp_file = temp_file.to_str().expect("utf-8 file name");
46
47 protoc.write_descriptor_set(protoc::DescriptorSetOutArgs {
48 out: temp_file,
49 includes: args.includes,
50 input: args.input,
51 include_imports: true,
52 })?;
53
54 let mut fds = Vec::new();
55 let mut file = fs::File::open(temp_file)?;
56 file.read_to_end(&mut fds)?;
57
58 drop(file);
59 drop(temp_dir);
60
61 let fds: protobuf::descriptor::FileDescriptorSet =
62 protobuf::parse_from_bytes(&fds).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
63
64 let mut includes = args.includes;
65 if includes.is_empty() {
66 static DOT_SLICE: &'static [&'static str] = &["."];
67 includes = DOT_SLICE;
68 }
69
70 let mut files_to_generate = Vec::new();
71 'outer: for file in args.input {
72 for include in includes {
73 if let Some(truncated) = remove_path_prefix(file, include) {
74 files_to_generate.push(truncated.to_owned());
75 continue 'outer;
76 }
77 }
78
79 return Err(Error::new(
80 io::ErrorKind::Other,
81 format!(
82 "file {:?} is not found in includes {:?}",
83 file, args.includes
84 ),
85 ));
86 }
87
88 let gen_result = grpcio_compiler::codegen::gen(fds.get_file(), &files_to_generate);
89
90 for r in gen_result {
91 let r: protobuf::compiler_plugin::GenResult = r;
92 let file = format!("{}/{}", args.out_dir, r.name);
93 let mut file = fs::File::create(&file)?;
94 file.write_all(&r.content)?;
95 file.flush()?;
96 }
97
98 Ok(())
99}
100
101fn remove_dot_slash(path: &str) -> &str {
102 if path == "." {
103 ""
104 } else if path.starts_with("./") || path.starts_with(".\\") {
105 &path[2..]
106 } else {
107 path
108 }
109}
110
111fn remove_path_prefix<'a>(mut path: &'a str, mut prefix: &str) -> Option<&'a str> {
112 path = remove_dot_slash(path);
113 prefix = remove_dot_slash(prefix);
114
115 if prefix == "" {
116 return Some(path);
117 }
118
119 if prefix.ends_with("/") || prefix.ends_with("\\") {
120 prefix = &prefix[..prefix.len() - 1];
121 }
122
123 if !path.starts_with(prefix) {
124 return None;
125 }
126
127 if path.len() <= prefix.len() {
128 return None;
129 }
130
131 if path.as_bytes()[prefix.len()] == b'/' || path.as_bytes()[prefix.len()] == b'\\' {
132 return Some(&path[prefix.len() + 1..]);
133 } else {
134 return None;
135 }
136}
137
138#[cfg(test)]
139mod test {
140 #[test]
141 fn remove_path_prefix() {
142 assert_eq!(
143 Some("abc.proto"),
144 super::remove_path_prefix("xxx/abc.proto", "xxx")
145 );
146 assert_eq!(
147 Some("abc.proto"),
148 super::remove_path_prefix("xxx/abc.proto", "xxx/")
149 );
150 assert_eq!(
151 Some("abc.proto"),
152 super::remove_path_prefix("../xxx/abc.proto", "../xxx/")
153 );
154 assert_eq!(
155 Some("abc.proto"),
156 super::remove_path_prefix("abc.proto", ".")
157 );
158 assert_eq!(
159 Some("abc.proto"),
160 super::remove_path_prefix("abc.proto", "./")
161 );
162 assert_eq!(None, super::remove_path_prefix("xxx/abc.proto", "yyy"));
163 assert_eq!(None, super::remove_path_prefix("xxx/abc.proto", "yyy/"));
164 }
165}