blob: 859fa5e0516307f4de9797bfe71ea0f483fc342d [file] [log] [blame]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07001//! This crate implement protobuf codegen.
2//!
3//! This crate:
4//! * provides `protoc-gen-rust` plugin for `protoc` command
5//! * implement protobuf codegen
6//!
7//! This crate is not meant to be used directly, in fact, it does not provide any public API
8//! (except for `protoc-gen-rust` binary).
9//!
10//! Code can be generated with either:
11//! * `protoc-gen-rust` binary or
12//! * `protoc-rust` crate (codegen which depends on `protoc` binary for parsing)
13//! * `protobuf-codegen-pure` crate
14
Haibo Huangba676d32020-08-12 13:52:13 -070015#![deny(broken_intra_doc_links)]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070016#![deny(missing_docs)]
17
18extern crate protobuf;
19
20use std::collections::hash_map::HashMap;
21use std::fmt::Write as FmtWrite;
22use std::fs::File;
23use std::io;
24use std::io::Write;
25use std::path::Path;
26
27use protobuf::compiler_plugin;
28use protobuf::descriptor::*;
29use protobuf::Message;
30
31mod customize;
32mod enums;
33mod extensions;
34mod field;
35mod file;
36mod file_and_mod;
37mod file_descriptor;
38#[doc(hidden)]
39pub mod float;
40mod inside;
41mod message;
42mod oneof;
43mod protobuf_name;
44mod rust_name;
45mod rust_types_values;
46mod serde;
47mod well_known_types;
48
49pub(crate) mod rust;
50pub(crate) mod scope;
51pub(crate) mod strx;
52pub(crate) mod syntax;
53
54use customize::customize_from_rustproto_for_file;
55#[doc(hidden)]
56pub use customize::Customize;
57
58pub mod code_writer;
59
60use self::code_writer::CodeWriter;
61use self::enums::*;
62use self::extensions::*;
63use self::message::*;
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070064use inside::protobuf_crate_path;
65use scope::FileScope;
66use scope::RootScope;
67
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -070068use crate::file::proto_path_to_rust_mod;
69
Haibo Huangba676d32020-08-12 13:52:13 -070070#[doc(hidden)]
71pub use protobuf_name::ProtobufAbsolutePath;
72#[doc(hidden)]
73pub use protobuf_name::ProtobufIdent;
74#[doc(hidden)]
75pub use protobuf_name::ProtobufRelativePath;
76
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070077fn escape_byte(s: &mut String, b: u8) {
78 if b == b'\n' {
79 write!(s, "\\n").unwrap();
80 } else if b == b'\r' {
81 write!(s, "\\r").unwrap();
82 } else if b == b'\t' {
83 write!(s, "\\t").unwrap();
84 } else if b == b'\\' || b == b'"' {
85 write!(s, "\\{}", b as char).unwrap();
86 } else if b == b'\0' {
87 write!(s, "\\0").unwrap();
88 // ASCII printable except space
89 } else if b > 0x20 && b < 0x7f {
90 write!(s, "{}", b as char).unwrap();
91 } else {
92 write!(s, "\\x{:02x}", b).unwrap();
93 }
94}
95
96fn write_file_descriptor_data(
97 file: &FileDescriptorProto,
98 customize: &Customize,
99 w: &mut CodeWriter,
100) {
101 let fdp_bytes = file.write_to_bytes().unwrap();
102 w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
103 w.indented(|w| {
104 const MAX_LINE_LEN: usize = 72;
105
106 let mut s = String::new();
107 for &b in &fdp_bytes {
108 let prev_len = s.len();
109 escape_byte(&mut s, b);
110 let truncate = s.len() > MAX_LINE_LEN;
111 if truncate {
112 s.truncate(prev_len);
113 }
114 if truncate || s.len() == MAX_LINE_LEN {
115 write!(s, "\\").unwrap();
116 w.write_line(&s);
117 s.clear();
118 }
119 if truncate {
120 escape_byte(&mut s, b);
121 }
122 }
123 if !s.is_empty() {
124 write!(s, "\\").unwrap();
125 w.write_line(&s);
126 s.clear();
127 }
128 });
129 w.write_line("\";");
130 w.write_line("");
Haibo Huangba676d32020-08-12 13:52:13 -0700131 w.lazy_static(
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700132 "file_descriptor_proto_lazy",
133 &format!(
134 "{}::descriptor::FileDescriptorProto",
135 protobuf_crate_path(customize)
136 ),
Haibo Huangba676d32020-08-12 13:52:13 -0700137 customize,
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700138 );
139 w.write_line("");
140 w.def_fn(
Haibo Huangba676d32020-08-12 13:52:13 -0700141 &format!(
142 "parse_descriptor_proto() -> {}::descriptor::FileDescriptorProto",
143 protobuf_crate_path(customize)
144 ),
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700145 |w| {
Haibo Huangba676d32020-08-12 13:52:13 -0700146 w.write_line(&format!(
Haibo Huangb7f1d002021-01-07 18:06:37 -0800147 "{}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
Haibo Huangba676d32020-08-12 13:52:13 -0700148 protobuf_crate_path(customize)
149 ));
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700150 },
151 );
152 w.write_line("");
153 w.pub_fn(
Haibo Huangba676d32020-08-12 13:52:13 -0700154 &format!(
155 "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
156 protobuf_crate_path(customize)
157 ),
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700158 |w| {
Haibo Huang72fec012020-07-10 20:24:04 -0700159 w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
160 w.write_line("parse_descriptor_proto()");
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700161 });
162 },
163 );
164}
165
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700166struct GenFileResult {
167 compiler_plugin_result: compiler_plugin::GenResult,
168 mod_name: String,
169}
170
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700171fn gen_file(
172 file: &FileDescriptorProto,
173 _files_map: &HashMap<&str, &FileDescriptorProto>,
174 root_scope: &RootScope,
175 customize: &Customize,
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700176) -> GenFileResult {
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700177 // TODO: use it
178 let mut customize = customize.clone();
179 // options specified in invocation have precedence over options specified in file
180 customize.update_with(&customize_from_rustproto_for_file(file.get_options()));
181
182 let scope = FileScope {
183 file_descriptor: file,
184 }
185 .to_scope();
186 let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
187 file.get_options().get_optimize_for() == FileOptions_OptimizeMode::LITE_RUNTIME
188 });
189
190 let mut v = Vec::new();
191
192 {
193 let mut w = CodeWriter::new(&mut v);
194
Haibo Huangb7f1d002021-01-07 18:06:37 -0800195 w.write_generated_by("rust-protobuf", "2.20.0");
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700196 w.write_line(&format!("//! Generated file from `{}`", file.get_name()));
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700197 if customize.inside_protobuf != Some(true) {
198 w.write_line("");
199 w.write_line("/// Generated files are compatible only with the same version");
200 w.write_line("/// of protobuf runtime.");
201 w.commented(|w| {
202 w.write_line(&format!(
203 "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
204 protobuf_crate_path(&customize),
205 protobuf::VERSION_IDENT
206 ));
207 })
208 }
209
210 for message in &scope.get_messages() {
211 // ignore map entries, because they are not used in map fields
212 if message.map_entry().is_none() {
213 w.write_line("");
214 MessageGen::new(message, &root_scope, &customize).write(&mut w);
215 }
216 }
217 for enum_type in &scope.get_enums() {
218 w.write_line("");
219 EnumGen::new(enum_type, file, &customize, root_scope).write(&mut w);
220 }
221
222 write_extensions(file, &root_scope, &mut w, &customize);
223
224 if !lite_runtime {
225 w.write_line("");
226 write_file_descriptor_data(file, &customize, &mut w);
227 }
228 }
229
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700230 GenFileResult {
231 compiler_plugin_result: compiler_plugin::GenResult {
232 name: format!("{}.rs", proto_path_to_rust_mod(file.get_name())),
233 content: v,
234 },
235 mod_name: proto_path_to_rust_mod(file.get_name()).into_string(),
236 }
237}
238
239fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult {
240 let mut v = Vec::new();
241 let mut w = CodeWriter::new(&mut v);
242 w.comment("@generated");
243 w.write_line("");
244 for m in mods {
245 w.write_line(&format!("pub mod {};", m));
246 }
247 drop(w);
248 compiler_plugin::GenResult {
249 name: "mod.rs".to_owned(),
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700250 content: v,
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700251 }
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700252}
253
254// This function is also used externally by cargo plugin
255// https://github.com/plietar/rust-protobuf-build
256// So be careful changing its signature.
257#[doc(hidden)]
258pub fn gen(
259 file_descriptors: &[FileDescriptorProto],
260 files_to_generate: &[String],
261 customize: &Customize,
262) -> Vec<compiler_plugin::GenResult> {
263 let root_scope = RootScope {
264 file_descriptors: file_descriptors,
265 };
266
267 let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
268 let files_map: HashMap<&str, &FileDescriptorProto> =
269 file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
270
271 let all_file_names: Vec<&str> = file_descriptors.iter().map(|f| f.get_name()).collect();
272
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700273 let mut mods = Vec::new();
274
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700275 for file_name in files_to_generate {
276 let file = files_map.get(&file_name[..]).expect(&format!(
277 "file not found in file descriptors: {:?}, files: {:?}",
278 file_name, all_file_names
279 ));
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700280
281 let gen_file_result = gen_file(file, &files_map, &root_scope, customize);
282 results.push(gen_file_result.compiler_plugin_result);
283 mods.push(gen_file_result.mod_name);
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700284 }
Chih-Hung Hsieh07f32d92020-10-06 01:50:33 -0700285
286 if customize.gen_mod_rs.unwrap_or(false) {
287 results.push(gen_mod_rs(&mods));
288 }
289
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700290 results
291}
292
293#[doc(hidden)]
294pub fn gen_and_write(
295 file_descriptors: &[FileDescriptorProto],
296 files_to_generate: &[String],
297 out_dir: &Path,
298 customize: &Customize,
299) -> io::Result<()> {
300 let results = gen(file_descriptors, files_to_generate, customize);
301
302 for r in &results {
303 let mut file_path = out_dir.to_owned();
304 file_path.push(&r.name);
305 let mut file_writer = File::create(&file_path)?;
306 file_writer.write_all(&r.content)?;
307 file_writer.flush()?;
308 }
309
310 Ok(())
311}
312
313#[doc(hidden)]
314pub fn protoc_gen_rust_main() {
315 compiler_plugin::plugin_main_2(|r| {
316 let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
317 gen(r.file_descriptors, r.files_to_generate, &customize)
318 });
319}
Haibo Huangba676d32020-08-12 13:52:13 -0700320
321/// Used in protobuf-codegen-identical-test
322#[doc(hidden)]
323pub fn proto_name_to_rs(name: &str) -> String {
324 format!("{}.rs", proto_path_to_rust_mod(name))
325}